模板非类型参数,C++11,字符串文字的限制

Template Non-Type argument, C++11, restriction for string literals

模板非类型参数的限制规则说:

非类型、非模板模板参数的模板参数应为以下之一:

——对于非整型或枚举类型的模板参数,模板参数类型的转换常量表达式(5.19);或者

—非类型模板参数的名称;或者

——常量表达式(5.19),指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板-id,但不包括非静态class 成员,表示(忽略括号)为 & 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 作为非类型模板参数。

但现在指向具有内部链接(静态存储)的对象的指针也允许用作非类型模板参数,并且字符串文字具有内部链接。

根据您的问题,最接近允许的字符串文字是:

a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage

字符串文字既没有外部链接也没有内部链接,因此不允许。

如果您有多个翻译单元,每个都包含一个带有内部链接的 const char arr[5]; 定义,那么这些都是不同的对象,具有不同的地址,但在一个翻译单元中,arr == arr,总是。实现弄清楚了如何使它适用于模板参数。

如果您有多个翻译单元,每个翻译单元都包含 "1234",那么这些翻译单元 不会 保证具有不同的地址。然而,即使在单个翻译单元中,它们也保证具有相同的地址。

如果 "1234" != "1234",那么引用模板 S<"1234"> 毫无意义:您每次都会引用不同的模板实例。

如果 "1234" == "1234",那么确保 S<"1234"> 在每个翻译单元中是相同类型的实现变得复杂。

非常有趣的是,以下代码是有效的 C++11,但字符串文字不起作用。对于 C++14,您甚至可以删除 static_cast.

#include <iostream>

static constexpr const char s1[] = "Hello World!";
static const char s2[] = __DATE__ " " __TIME__;

template< const char* STR > struct X {X() {std::cout << STR << std::endl;}};
X< s1 > x1;
X< static_cast<const char*>(s2) > x2;

int main() {}

即使当今的 C++ 编译器不接受字符串文字,在某些情况下允许这样做也非常方便。

接受临时 class 实例化的地址作为 NT 模板参数将朝着类似的方向发展:

#include <iostream>

struct S {int s;};
static constexpr const S s1{123};
static const S s2{999};

template< const S* _S > struct X {X() {std::cout << _S->s << std::endl;}};
X< &s1 > x1;
X< &s2 > x2;
//NOT OK: X< (&S{1}) > x3;

int main() {}