C++ 模板特化
[toc]
前言
本文仅讲述类型的特化,不讲范围的特化
关键字:模板、泛化、特化(全特化、偏特化)
函数模板及其特化
示例
先看下面的例子:(D01)
template<typename T>
T max(T t1, T t2) {
return (t1 > t2) ? t1 : t2;
}
int main() {
int a = 1;
int b = 2;
std::cout << max(a, b) << std::endl;
const char *d = "b";
const char *c = "a";
std::cout << max(c, d) << std::endl;
return 0;
}
// 输出如下
// 2
// a
按理说应该是 b 比 a 大,为什么会输出 a 呢?原来字符串比较的时候是直接比较的指针地址
解决方法,加上以下特化版本即可
template<>
const char *max<const char *>(const char *t1, const char *t2) {
return (strcmp(t1, t2) > 0) ? t1 : t2;
}
有些文章可能会推荐使用函数重载来实现函数模板的特化,如刚刚的特化版本可以写为以下重载形式
const char *max(const char *t1, const char *t2) {
return (strcmp(t1, t2) > 0) ? t1 : t2;
}
这样写如果是安按照代码(D01)中的方式调用是没问题的;但是万一将 max(c, d)
调用换成 max<const char *>(c, d)
显示调用就会出问题,这时会去调用默认的泛化版本,所以还是推荐使用模板特化版本
说明
函数模板及成员函数均不支持偏特化,仅支持全特化
类模板及其特化
泛化类
template<typename A, typename B>
class Foo {
public:
Foo(A &&a, B &&b) : a(a), b(b) {}
void PrintA() {
std::cout << "A: " << a << std::endl;
}
void PrintB() {
std::cout << "B: " << b << std::endl;
}
void Print() {
std::cout << "A: " << a << " B: " << b << std::endl;
}
void Exec() {
++a;
++b;
}
private:
A a;
B b;
};
特化类
假设 double 类型的 Exec 不是自增,二是乘以 1.5,顾需要特化此模板
// 偏特化 A 版本
template<typename B>
class Foo<double, B> {
public:
Foo(double a, B &&b) : a(a), b(b) {}
void PrintA() {
std::cout << "A: " << a << std::endl;
}
void PrintB() {
std::cout << "B: " << b << std::endl;
}
void Print() {
std::cout << "A: " << a << " B: " << b << std::endl;
}
void Exec() {
a *= 1.5;
++b;
}
private:
double a;
B b;
};
// 偏特化 B 版本
template<typename A>
class Foo<A, double> {
public:
Foo(A &&a, double b) : a(a), b(b) {}
void PrintA() {
std::cout << "A: " << a << std::endl;
}
void PrintB() {
std::cout << "B: " << b << std::endl;
}
void Print() {
std::cout << "A: " << a << " B: " << b << std::endl;
}
void Exec() {
++a;
b *= 1.5;
}
private:
A a;
double b;
};
// 全特化版本
template<>
class Foo<double, double> {
public:
Foo(double a, double b) : a(a), b(b) {}
void PrintA() {
std::cout << "A: " << a << std::endl;
}
void PrintB() {
std::cout << "B: " << b << std::endl;
}
void Print() {
std::cout << "A: " << a << " B: " << b << std::endl;
}
void Exec() {
a *= 1.5;
b *= 1.5;
}
private:
double a;
double b;
};
说明
三个特化版本中,其实 PrintA PrintB Print
三个函数实现是完全一样的,但是类模板特化时就还是需要定义出来,这里倒是有一种方法能偷一点懒:成员函数特化
// 偏特化 B 版本 编译不过
template<B>
void Foo<double, B>::Exec() {
a *= 1.5;
++b;
}
// 偏特化 B 版本 编译不过
template<A>
void Foo<A, double>::Exec() {
++a;
b *= 1.5;
}
// 全特化版本
template<>
void Foo<double, double>::Exec() {
a *= 1.5;
b *= 1.5;
}
理想很丰满,现实很骨感,只有全特化版本能编译通过
小结
类的特化需要将所有成员函数都重新实现一遍
函数模板及成员函数均不支持偏特化,仅支持全特化;且函数的全特化版本直接写在头文件的话,会出现多重定义;解决方案有两个,一是仅在头文件申明全特化版本在源文件实现,二是在函数前面加上 inline
关键字
由于个人水平有限,文中若有不合理或不正确的地方欢迎指出改正