C++模板特化示例和总结
在学习元编程过程中,需要用到模板递归,函数模板和类模板的递归方式略有差别,主要是由于二者特化语法的差别导致的。通过一些例子整理了函数模板和类模板的特化语法和差异。
要点
- c++函数模板不支持偏特化,包括类型偏特化、值偏特化都不支持。
- 多个参数的函数模板,不能通过特化在模板递归中设置递归的终止条件。
- 单个值数值参数的函数模板,可以通过模板特化实现递归终止。此时的是完全特化函数模板,这是允许的。
- 不允许函数模板偏特化,对函数的能力影响不大,函数可以通过重载提供对各种类型变化的支持。
1. 函数模板递归
1.1 函数模板用constexpr if实现递归
// 函数模板不支持偏特化,不能通过模板设置递归终止,可以用constexpr if终止递归
template <typename T, int n>
void printTuple(const T& t) {if constexpr (n > 1) {printTuple<T, n - 1>(t);}cout << get<n-1>(t) << endl;
}// 不允许偏特化函数模板,编译错误
//template <typename T, int n>
//void printTuple <T, 0> (const T& t) {
//}// 给n提供默认值,简化调用
template <typename T, int n=tuple_size<T>::value>
void printTuple2(const T& t) {if constexpr (n > 1) {printTuple<T, n - 1>(t);}cout << get<n - 1>(t) << endl;
}int main()
{tuple<int, double, const char*, string> tp{1, 2.0, "hi", "boy"};using atuple = tuple<int, double, const char*, string>;printTuple<atuple, tuple_size<atuple>::value>(tp); printTuple2<atuple>(tp);
}
1.2 函数模板通过完全特化实现递归
// 利用函数模板完全特化实现递归展开
template<int N>
int factorial() {return N * factorial<N-1>();
}template<>
int factorial<0>() {return 1;
}int main() {std::cout << factorial<5>() << std::endl; // 输出:120return 0;
}
2 类模板递归
2.1 类模板通过偏特化实现递归
类模板可以实现多个参数的模板偏特化,偏特化类型参数、值参数都支持。
// 通过类模板特化实现递归终止
// 也可以使用constexpr if实现终止逻辑,这样就不用特化终止条件的类模板class TuplePrint<T, 0>
// 这里的n也可以提供默认值tuple_size<T>::value
template <typename T, int n>
class TuplePrint
{
public:TuplePrint(const T& t) {//if constexpr (n > 1) {TuplePrint<T, n - 1> tp{ t };//}cout << get<n - 1>(t) << endl;}
};template <typename T>
class TuplePrint<T, 0>
{
public:TuplePrint(const T& t) {}
};int main()
{tuple<int, double, const char*, string> tp{1, 2.0, "hi", "boy"};using atuple = tuple<int, double, const char*, string>;TuplePrint<atuple, tuple_size<atuple>::value> tpr{tp};
}
2.2 类模板通过constexpr if实现递归
// 使用constexpr if实现终止逻辑,这样就不用特化终止条件的类模板class TuplePrint<T, 0>
// 这里的n也可以提供默认值tuple_size<T>::value
template <typename T, int n>
class TuplePrint
{
public:TuplePrint(const T& t) {if constexpr (n > 1) {TuplePrint<T, n - 1> tp{ t };}cout << get<n - 1>(t) << endl;}
};int main()
{tuple<int, double, const char*, string> tp{1, 2.0, "hi", "boy"};using atuple = tuple<int, double, const char*, string>;TuplePrint<atuple, tuple_size<atuple>::value> tpr{tp};
}
3 模板和特化示例
3.1 类模板的偏特化例子
// 工具方法,打印3个参数,简化类实现
void print(auto a, auto b, auto n) {cout << endl;cout << "a: " << typeid(a).name() << endl;cout << "b: " << typeid(b).name() << endl;cout << "n: " << n << endl;
}template <typename Ta, typename Tb, int n>
class oneclass
{
public:oneclass(const Ta& a, const Tb& b) {print(a, b, n);}
};template <typename Ta, typename Tb>
class oneclass<Ta, Tb, 10>
{
public:oneclass(const Ta& a, const Tb& b) {print(a, b, 10);cout << "specialization for 10 :)" << endl;}
};template <typename Ta, int n>
class oneclass<Ta, int, n>
{
public:oneclass(const Ta& a, const int& b) {print(a, b, n);cout << "specialization for Tb :)" << endl;}
};template <typename Tb, int n>
class oneclass<int, Tb, n>
{
public:oneclass(const int& a, const Tb& b) {print(a, b, n);cout << "specialization for the 1st type Ta :)" << endl;}
};int main()
{oneclass<double, const char*, 2> oc1 { 1.0, "hi" };oneclass<double, const char*, 10> oc2 { 2.0, "hi" };oneclass<double, int, 11> oc3 { 1.0, 3 };oneclass<int, const char*, 11> oc4 { 4, "hi" };
}
运行结果:
// oneclass<double, const char*, 2> oc1 { 1.0, "hi" }; 输出
a: double
b: char const * __ptr64
n: 2// oneclass<double, const char*, 10> oc2 { 2.0, "hi" }; 输出
a: double
b: char const * __ptr64
n: 10
specialization for 10 :)// oneclass<double, int, 11> oc3 { 1.0, 3 }; 输出
a: double
b: int
n: 11
specialization for Tb :)// oneclass<int, const char*, 11> oc4 { 4, "hi" }; 输出
a: int
b: char const * __ptr64
n: 11
specialization for the 1st type Ta :)
3.2 函数模板特化例子
template <typename Ta, typename Tb>
void printSum(Ta a, Tb b) {cout << "general: sum=" << a + b << endl;
}// 函数模板不支持偏特化,error
//template <typename Ta>
//void printSum<Ta, int>(const Ta& a, const int& b) {
// cout << "T&int: sum=" << a + b << endl;
//}template <>
void printSum<int, int>(int a, int b) {cout << "int: sum=" << a + b << endl;
}template <typename T>
void printSum(T a, T b) {cout << "T: sum=" << a + b << endl;
}int main()
{printSum(1.0f, 1); // general: sum=2, printSum<float, int>printSum(1.0, 2.0); // T: sum=3, printSum<double, double>printSum(1, 3); // T: sum=4, printSum<T, T>,这里有限调用<T, T>版本,除非像下面显示调用<int, int>的函数,printSum<int, int>(1, 4); // int: sum=5, printSum<int, int>printSum<double, int>(1, 5); // general: sum=6, printSum<double, int>
}
4 总结特化语法
- 类型参数特化为具体类型。
- 非类型参数,值参数,特化为具体数值。
- 模板参数列表定义在template关键字后<>中,特化参数列表定义在模板名称(类名或函数名)后的<>中。
- 对于特化的模板,模板参数列表中去掉特化的参数。
- 在类定义或者函数定义中,用特化的类型名替换对应的通用类型参数名,用具体的特化值,替换对应的非类型参数名。特化的通用类型和非类型参数名,不再有效。
4.1 类模板的特化
// 类模板的定义和特化
template<模板参数列表,包括类型参数、非类型参数>
class ClassName { ... }// 完全特化
template<>
class ClassName<特化模板参数列表,包括具体类型、非类型参数的具体值> { ... }// 部分特化,偏特化
template<未特化的模板参数列表,包括类型、非类型参数>
class ClassName<未特化的类型或变量名, 特化的具体类型名或具体值> { ... }
4.2 函数模板特化
// 函数模板的定义和特化
template<模板参数列表,包括类型参数、非类型参数>
returnType func { ... }// 完全特化函数模板
template<>
returnType func<特化模板参数列表,包括具体类型、非类型参数的具体值> { ... }