CTTCG 03 Nontype Template Parameters
模板参数也可以不是类型,而是值。
非类型参数推导只对直接调用有效
模板函数的类型推导只对立即调用有效,而对算法模板这类需要提前知道类型信息的场合无效。比如:
std::transform (source.begin(), source.end(), // start and end of source
dest.begin(), // start of destination
addValue<5,int>); // operation
上面代码中尽管 addValue
的参数可能可以推导出来,但由于 std::transform
要求提前知道函数的类型,去掉参数之后代码无法编译。
非类型参数的类型限制
非类型参数不能是浮点数(未来可能可以)。只能是整数、nullptr、指针或左值引用。
2017 年的 P1714 提案建议增加浮点数,目前找不到后续。不过,gcc 13 在 -std=c++20
时已经提供了浮点数参数功能,clang 暂不支持。
作为对比,如今 C++20 中某些非 union class type 已经可以作为模板参数,但浮点数不能。
当参数是指针或引用时,不可以指向字符串字面量、临时值、数据成员或其他子对象;C++11 还要求对象有 external linkage,C++14 要求对象有 external linkage 或者 internal linkage,C++17 对 linkage 没有要求。
虽然不能使用字符串字面量,但可以使用编译期可定的字符数组的引用作为参数。
extern char const s03[] = "hi"; // external linkage
char const s11[] = "hi"; // internal linkage
int main()
{
Message<s03> m03; // OK (all versions)
Message<s11> m11; // OK since C++11
static char const s17[] = "hi"; // no linkage
Message<s17> m17; // OK since C++17
}
非类型参数可能需要括号包裹
模板参数中出现表达式时,要使用括号,否则容易出现尖括号被理解成模板参数结束标志,导致无法编译的情况:C<42, sizeof(int) > 4> c;
,应该改成 C<42, (sizeof(int) > 4)> c;
auto 作为非类型参数
由于非类型参数的通用限制,auto &&
是不允许使用的(非类型参数不能有右值)。允许的有:auto
/ auto &
/ auto const &
/ decltype(auto)
。当实际类型为引用时,要求传入的值在编译期具有可计算出的不变地址(全局常量和变量符合要求、纯右值的数字字面量不符合要求)。当实际类型为值时,需要传入的值在编译器有可计算出的不变值(全局常量和数字字面量符合要求)。
指定非类型参数的类型是 auto 时,仍然可以通过 decltype 手段获得参数,也有了自动推导类型的优势。比如可以编写一个原地分配内存的 Stack 类:
Stack<int, 20u> stk1;
Stack<int, 20> stk2;
这样两个 Stack 对象的 size_type
不同。
另外, decltype(auto)
可以表示类型完美转发的场景。
template<decltype(auto) N>
class C {
//...
};
int i;
C<(i)> x; // N is int&
上面的代码如果写成 C<i> x
或者模板参数是 auto N
,N 的类型就是 int 而不是引用类型。
💡 既然模板参数要求编译期计算完成,好像在参数中传递引用没有什么意义?