模板的类型参数, 除了能够定义默认类型之外.
还可以定义匿名类型参数, 这个匿名类型参数不能被函数体使用, 也不能被返回值使用.
匿名类型参数利用替换失败不是错误
的特性来判定, 如果没有出现替换失败, 那么就是符合匹配的.
什么场景下需要用到匿名类型参数?
拿 C++ 模板 SFINAE 这篇文章中第二份代码中的一个片段来举例.
这个函数声明了 class U
这个类型参数, 并且设定了一个默认值T::author_type
,
但是函数体、返回值中都没有使用到 U
这个类型, 如果实际项目中函数体的代码非常复杂,
多出一个不明确的类型是非常恐怖的一件事情, 而匿名类型参数可以减少这个心里负担.
template<typename T, class U = typename T::author_type>
void example(T t) {
cout << "template<typename T = book::author_type > void example();" << endl;
}
所以, 匿名类型参数就是用来降低心里复杂度的蜜枣.
由于 struct book
没有定义 author_type
这个成员类型, 因此 T::author_type
是一个无效的调用, 编译器不会报错, 而是选择跳过当前匹配进而去寻找其他匹配.
#include <iostream>
using std::cout;
using std::endl;
struct book {
typedef int page_type;
using name_type = string;
};
// class = typename T::author_type 就是一个匿名类型参数.
template<typename T, class = typename T::author_type>
void example(T t) {
cout << "template<typename T = book::author_type > void example();" << endl;
}
// template<typename T, class = typename T::page_type>
// 等同于
// template<typename T, typename = int>
// 第二个类型参数是一个匿名类型参数(没有提供一个identifier), 并且提供了一个int作为默认类型.
template<typename T, class = typename T::page_type>
void example(T t) {
cout << "void example(book b)" << endl;
}
int main(void) {
book b;
example(b);
return 0;
}
展开讨论模板参数的定义方式
-
什么是 Identifier?
template <typename T> // T 就是 Identifier. void example() {};
-
Identifier 的命名不能跟 C++ 中的关键字出现冲突.
// 错误的 // 因为 identifier 占用和覆盖了 int , 会导致后续功能不正常, 因此编译是不会通过的. template<typename int> void normal(int s){ cout << "normal: " << s << endl; } // 正确的 // 但是建议使用常见的 T 或者 U 来当作 Identifier. template<typename aaa> void normal(aaa s){ cout << "normal: " << s << endl; }
template <>
中typename
和class
有什么区别?
没有区别, 但是建议统一风格, 只使用其中任意一种,
我个人基本都是使用typename
.typename
这个关键字是什么含义?
它是一个标识符, 即便不放在 template <> 中, 也可以再常规的函数体中使用, 例如:typename int a = 10;
但是在template<>
中, 要声明一个类型, 必须要使用typename
或class
标识符, 用来标识它是一个Identifier.-
具体化和匿名类型参数的区别?
具体化有一个Identifier
而匿名类型参数没有Identifier
.#include <iostream> using std::cout; using std::endl; // 具体化 // int 是一个具体的类型 // n 是一个Identifier 也是一个具体值, 所以不能用 n 来声明一个变量. template<int n> // 也可以写成: template<typename int n> void specialization() { cout << n << endl; }; // 具体化 和 匿名类型参数 // 因为 int 是一个具体类型, 所以不能也没必要定义一个 Identifier , 所以函数体无法使用这个参数. // 匿名类型参数必须要有一个默认值. template<int = 0> // 也可以写成: template<typename int = 0> void specialization_and_anonymous() { cout << "specialization_and_anonymous" << endl; }; int main(void) { specialization<10>(); // 必须这样使用. specialization_and_anonymous(); // output: // 10 // specialization_and_anonymous return 0; }