模板非类型参数的限制规则说:
非类型非模板模板参数的模板参数应为以下之一:
- 对于整数或枚举类型的非类型模板参数,是模板参数类型的转换常量表达式(5.19);或
- 非类型模板参数的名称;或
- 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址,或具有外部或内部链接的函数,包括函数模板和函数模板ID,但不包括非静态类成员,表达(忽略括号)为 &安培; id-expression,除了&如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或
- 一个求值为空指针值的常量表达式(4.10);或
- 一个求值为空成员指针值的常量表达式(4.11);或
- 指向成员的指针,如5.3.1所述。
2 [注意:字符串文字(2.14.5)不满足任何这些类别的要求,因此不是可接受的模板参数。
[ Example:
template<class T, const char* p> class X {
/ ... /
};
X<int, "Studebaker"> x1; // error: string literal as template-argument
const char p[] = "Vivisectionist";
X<int,p> x2; // OK
—end example ] —end note ]
那么为什么字符串文字不能用作非类型参数的参数?
const char arr[5] = "1234";
arr具有相同的类型 const char [5] 为
"1234";
arr具有外部链接,这就是为什么允许在c ++ 11标准之前使用arr作为非类型模板参数。
但是现在指向具有内部链接(静态存储)的对象的指针也允许用作非类型模板参数,而字符串文字具有内部链接。
答案 0 :(得分:3)
从你的问题来看,最接近字符串文字的是:
一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址
字符串文字既没有外部链接也没有内部链接,因此不允许使用它们。
如果您有多个翻译单元,每个单元都包含带有内部链接的const char arr[5];
定义,那么这些单元都是不同的对象,具有不同的地址,但始终位于单个翻译单元arr == arr
内。实现找到了如何使模板参数工作。
如果您有多个翻译单元,每个单元都包含"1234"
,则不保证具有不同的地址。然而,即使在单个翻译单元中,他们也 保证具有相同的地址。
如果"1234" != "1234"
,则引用模板S<"1234">
毫无意义:您每次都指的是不同的模板实例。
如果"1234" == "1234"
,那么实施就会变得复杂,以确保S<"1234">
在每个翻译单元中都是相同的类型。
答案 1 :(得分:0)
下面的有效代码示例也很有趣:
#include <iostream>
template<const char* STR>
void print() {std::cout << STR << std::endl;}
const char str[] = "HELLO WORLD!";
int main()
{
//fails: print<reinterpret_cast<const char*>("HELLO WORLD!")>();
print<reinterpret_cast<const char*>(str)>();
}
最后,编译器会将内联字符串文字(如在注释行中)放置为 .rodata 部分中的新变量。每次都会导致发出新的临时变量和引用该变量地址的新模板实例。对于内容相等的常量字符串文字,在链接时优化之后,所有可变地址以后都将指向相同的地址。即使当今的C ++编译器不允许这样做(很可能是因为没有充分的理由或存在一些隐藏的极端情况),但似乎有可能允许这种构造。在某些情况下,这种功能可以帮助简化编译时配置。
将临时类实例化的地址作为NT模板参数接受会朝着相似的方向发展:
#include <iostream>
struct S {int a, b, c;};
template<const S* P>
void print() {std::cout << P->a << P->b << P->c << std::endl;}
const S s{1, 2, 3};
int main()
{
//fails: print< &S{1, 2, 3} >();
print<&s>();
}