一、class 和 typename
如下例所示:
1 2 3 4 5 6
| template <typename T> void func(T t) {}
template <class T> void func(T t) {}
|
历史原因:class 是 C++98 引入模板时的关键字,typename 是后来加入的。现在更推荐使用 typename,因为它更准确地表达了”类型参数”的含义(不一定非得是类)。
唯一例外 :模板模板参数中只能用 class(C++17 后也可以用 typename)
1 2 3 4 5 6 7
| template <template <typename> class Container> struct MyClass {};
template <template <typename> typename Container> struct MyClass {};
|
二、typename 的用途
见下例:
1 2 3 4 5 6
| template <typename T> void printSize(const T& container) { T::size_type size = container.size(); cout << size << endl; }
|
编译器在解析 T::size_type 的时候,不知道 T 是什么,因为模板参数直到实例化的时候才能确定。按照 C++ 规则,如果编译器不能确定一个名称是类型,它就认为不是类型(默认当作成员变量或静态函数)。
解决方案:用 typename 明确告诉编译器“这是一个类型”。
1 2 3 4 5 6 7 8
| template <typename T> void printSize(const T& container) { typename T::size_type size = container.size(); cout << size << endl; }
vector<int> v = {1, 2, 3}; printSize(v);
|
三、消除模板成员调用歧义
类似 typename 的歧义也发生在调用模板成员函数时。见下例:
1 2 3 4 5 6
| template <typename T> void process(T& obj) { obj.templateMethod<int>(); }
|
编译器看到 < 和 >,可能认为这是小于号和大于号,而不是模板参数列表。
解决方案 :用 .template 明确告诉编译器。
1 2 3 4 5 6 7 8 9
| template <typename T> void process(T& obj) { obj.template templateMethod<int>(); T* ptr = &obj; ptr->template templateMethod<double>(); }
|
总结:
| 语法 |
用途 |
示例 |
typename |
声明从属类型名称 |
typename T::iterator |
.template |
调用模板成员函数 |
obj.template func() |
->template |
通过指针调用模板成员函数 |
ptr->template func() |
::template |
通过作用域运算符调用 |
T::template func() |
四、标准库中的应用
std::allocator_traits:
1 2 3 4 5 6 7 8 9
| template <typename T> struct allocator_traits { using pointer = typename T::pointer; template <typename U> using rebind = typename T::template rebind<U>; };
|
std::iterator_traits:
1 2 3 4 5 6 7
| template <typename Iter> struct iterator_traits { using iterator_category = typename Iter::iterator_category; using value_type = typename Iter::value_type; using difference_type = typename Iter::difference_type; };
|