Table of Contents generated with DocToc
对于类模板和函数模板来说,模板参数可以不是类型,而是值,称为非类型模板参数。而是类型的模板参数则称为模板类型参数。和模板类型参数一样,由用户来指定特定的模板参数,不过对于非类型模板参数来说,这个待制定的模板参数就不是类型而是值了。
其实并没有什么特殊的:
- 例子:
std::array<T, size>
。 - 不同的非类型模板参数值实例化的模板类类型是不同的。之间不能进行任何隐式或者显式类型转换,也不能相互赋值。(除非定义了相应的模板化构造函数)
函数模板中的非类型模板参数同理:
- 某些时候定义一簇函数时很有用:
template<int Val, typename T>
T addValue(T x)
{
return x + Val;
}
- 用在像C++标准库中的
transform
这种算法就非常方便。
std::transform(vec.begin(), vec.end(), dest.begin(), addValue<5, int>);
非类型模板参数有一些限制:
- 参数类型只能是整型常量(编译期能够确定)。
- 包括布尔、枚举、字符类型、任何长度的无符号或者有符号整型。
- 还包括对象、函数、成员指针,对象或者函数的左值引用,或者
std::nullptr_t
。 - 浮点类型和类对象都不能作为非类型模板参数。
- 当使用指针或者引用作为模板实参时,其引用对象不能是字符串字面值、临时变量、数据成员、或者其他子对象。
- C++11以及之前,对象需要有外部链接属性(extern或者非static全局对象)。
- C++14时,对象需要有外部或者内部链接。
- C++17之后,对象不需要有链接属性(静态局部变量也可以用)。
- 总体来说,每一代标准限制都在放宽。
- 现在来说,只需要具有静态生命周期即可。12章会有更详细的讨论。
- 通常来说,用指针、函数指针、成员指针(成员函数或者数据成员)、变量或者函数的引用作为模板参数相对来说用得比较少,但是确实有可能这样用。
- 最重要的一点是无论如何模板参数都必须是编译期常量。
- 特殊类型的非类型模板参数例子见:P049.Restrictions.cpp
避免非法表达式:
- 实例化模板时模板实参可以是任何编译期表达式,注意如果其中用到了
<>
号,则需要使用括号括起来。
C++17起,可以使用auto
定义非类型模板参数:
- 然后由模板实例化传入的非类型模板实参来推导出这个非类型模板参数具体是什么类型。
- 在模板内部要引用这个类型,则可以使用
decltype(arg)
。 - C++14起就可以使用
auto
作为返回值类型,让编译器推导。 - 使用
auto
作为非类型模板参数的类型占位符时,其他非类型模板参数的约束同样起作用。 - 甚至可以使用
decltype(auto)
作为占位符,此时不会退化,实例化时传入引用则会得到引用类型模板参数。(auto
会自动退化,decltype
得到的类型则不会退化)。 - 后续会阐述更多细节。
- 模板可以有非类型模板参数。
- 不能使用浮点数、类对象作为模板的非类型模板参数。
auto
可以用在模板参数列表中以推导非类型模板参数的具体类型。