模板参数不一定非得是某种具体的类型,也可以是常规数值。和类模板使用类型作为参数类似,可以使代码的另一些细节留到被使用时再确定,只是对非类型模板参数,待定的不再是类型,而是某个数值。在使用这种模板时需要显式的指出待定数值的具体值,之后代码会被实例化。
template <typename T, int size>
class Container
{
public:
std::array<T, size> arr;
};
Container<int, 5> c{1, 2, 3, 4, 5};
for(int i : c.arr)
{
cout << i << endl;
}
非类型模板参数也可以是默认值
template <typename T, int size = 5>
class Container
{
public:
std::array<T, size> arr;
};
Container<int> c{1, 2, 3, 4, 5};
for(int i : c.arr)
{
cout << i << endl;
}
template <int value, typename T>
T add(T t)
{
return t + value;
}
//两种都可以实例化
cout << add<1, double>(2.1) << endl;
cout << add<1>(2.1) << endl;
可以通过传入的非类型模板参数推断出返回类型:
template <auto value, typename T = decltype(value)>
T add(T t)
{
return t + value;
}
add<1>(2); //实例化 int add(int),返回 3
可以通过如下方式确保传入的非类型模板参数的类型和类型参数的类型一致:
template <typename T, T value = T{}>
T add(T t)
{
return t + value;
}
add<int, 1.1>(2) //错误,非类型模板参数和模板参数类型不一致
add<int, 1>(2) //正确
使用非类型模板参数是有限制的。通常它们只能是整形常量(包含枚举),指向objects/functions/members 的指针,objects或者functions的左值引用,或者是std::nullptr_t(nullptr decltype 出来的类型)。
浮点型数或者 class 类型的对象都不能作为非类型模板参数使用(c++20标准支持):
template <double value> //错误
void foo(){}
template <string s> //错误
void foo(){}
当传递对象的指针或者引用作为模板参数时,对象不能是字符串常量,临时变量或者数据成员以及其它子对象;在cpp17之前,c++版本的每次更新都会放宽以上限制,因此还有一些针对不同版本的限制:
template <char const* name>
class Test {};
Test<"hello"> t; //错误,内外链接都没有
使用技巧规避
template <char const* name>
class Test {};
extern char const externalLink[] = "hello";
char const internalLink[] = "hello";
Test<externalLink> external; //正确、有外部链接
Test<internalLink> internal; //正确,有内部链接
static char const noLink[] = "hello";
Test<noLink> no; //c++17 前错误,内外链接都没有
非类型模板参数可以是任何编译器表达式。比如:
template<int i, bool b>
class T {};
T< sizeof(int), sizeof(int) == 4 > t;
需要注意的是,如果在表达式中使用了 operator >,就必须将相应表达式放在括号中,否则 > 会被语法解析为模板参数列表末尾的尖括号,导致错误
T< sizeof(int), sizeof(int) > 4 > t; //大于号导致语法解析错误
T< sizeof(int), (sizeof(int) > 4) > t; //正确,表达式在括号中
从 C++17 开始,可以使用auto作为类型模板参数的类型
template<typename T, auto size>
class Container
{
public:
using sizeType = decltype(size);
std::array<T, size> arr;
auto getSize() const // c++14 后可使用返回类型推导 auto getSize()
{
return size;
}
};
Container<int, 5> c{ 1, 2, 3, 4, 5 };
Container<int, 5u> cu{ 1, 2, 3, 4, 5 };
auto cSize = c.getSize();
auto cuSize = cu.getSize();
//验证类型是否相同, c 为int, cu 为 unsigned int
if ( std::is_same< decltype(cSize), decltype(cuSize) >::value)
{
cout << "Same type." << endl;
}