C++ Template
C++中的模板
C++中的模板可分为class template和function template. 它们之间存在不同,例如,function template不能partially specialized(偏特化)
1 | template <class T, class U> T f(U obj); // Primary template |
模板实例化(Template Instantiation)
模板的实例化分为Explicit Instantiation和Implicit Instantiation.
Explicit Instantiation
1 | template<typename T> |
Implicit Instantiation
实际中,我们使用模板的大多数时候都是 implicit instantiation, 即我们一般不会像上面一样,显式地声明一个具体类型的模板实例,更常见的是直接声明一个模板实例(type)的对象(object)。
1 | template<int... A> class NonTypeVariadicTemplate {}; // should be in .h |
模板特化(template specialisation)
模板特化是C++泛型编程的一个重要技术,它的一个典型应用就是type trait的实现
全特化(full specialisation)
1 | template <typename T> |
偏特化(partial specialisation)
虽然模板类的全特化是一个重要的泛型编程技术,但有时候我们可能需要一个介于完全泛化和全特化之间的一个特化版本,这就是偏特化。下面的例子is_pointer<T>
,这里我们需要一个完全泛化的模板版本来处理所有T
不是一个指针类型的情形,而需要一个偏特化版本来处理所有T
是一个指针类型的情形。
1 | template <typename T> |
从上面两个例子is_boolean<T>
和is_pointer<T>
可以看出,全特化和偏特化的语法有明显区别,偏特化在类名后面的尖括号<>
里仍然包含模板参数,像 <T*>
, 而全特化类名后面的<>
里已经不包含任何模板参数 (如 T
),而全部是具体的类型, 像 <bool>
。
下面的例子是一个更复杂的偏特化的例子remove_bounds<T>
, 这个模板类仅仅定义一个与T
类型相同的typedef成员类型,但是高层的数组关联被移除了,这样的模板类可以完成一个类型的转化
1 | template <typename T> |
从这个例子可以看出:
-
偏特化版本的模板参数(形参)的个数不一定和默认模板的相等
- 即
<typename T, std::size_t N>
比默认模板多出了std::size_t N
- 即
-
偏特化版本的类名后面的模板参数(实参)的个数和类型必须匹配默认模板的参数的个数和类型
- 即
remove_bounds<T[N]>
中的T[N]
, 个数匹配指T[N]
对应主模板里T
, 类型匹配指T[N]
中的T
匹配主模板里的T
- 即
typename
vs class
大多数情况下,typename
和class
可以互换使用,但它们也有不能互换的时候,下面的情况只能使用typename
关键字:
1 | template<class Option> |
template parameters vs template arguments
template parameters之于template arguments相当于函数的形参之于实参。C++模板的通用语法是:
1 | template <parameter-list> declaration |
parameter-list中的每个形参都可以是下列3种中的任何一种:
- non-type template parameter
- type template parameter
- template template parameter
模板类的模板构造函数
模板类的模板构造函数,意思是一个模板类的一个构造函数也是一个模板函数。这种情况有两点需要注意:
- 模板类的模板参数名不能和构造函数的模板参数名相同 (template parameter cannot share the same name)
- 不能显式地指定模板构造函数的模板实参 (cannot explicitly specify template argument for constructor)
这种情况可以很好的体现template parameter和template argument的区别,请看下面的例子:
1 |
|
从这个例子看到模板构造函数被调用时本该指定的non-type template argument(size_t) NumNamedValues
并没有被指定, 原因是模板函数的template argument list应该紧跟在函数名后面,但是构建函数模板(类型转换函数模板conversion member function template也是同样情况)被调用的时候不使用函数名,所以没有一种方法可以显式地指定
- constructor member function template
- conversion member function template
的template argument list.
Constraints & Rules
- Explicit instantiation时,如果可以从function parameter推导出类型,可以省去template args.
- Function template或class template的member function的Explicit实例化不能使用
inline
和constexpr
关键字。 - Different template instantiations are distinct types.