假设我们有一个非const char *
非类型参数的模板函数,如下所示:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << '\n';
}
使用此模板不会有问题,因为MESSAGE
可以在编译时推断出日志,因此以下用途是合法的:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
但下面的内容不是(see here):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
上面代码生成的错误如下:
'{anonymous} :: namespace_const_message'的值在常量表达式中不可用
我不明白为什么namespace_const_message
在namespace_message
时不能用于常量表达式;如果我必须赌他们其中一个不能用于常数表达式,我会打赌没有常数,但是那个已经作为常量表达式的那个!
注意:'{anonymous} :: namespace_const_message'未被声明为'constexpr'
namespace_message
既未声明为constexpr
,也用于常量表达式,其值在编译时推导出来。如果表达式为constexpr
,为什么需要const
,如果不是const,则为什么不需要?
同样适用于匿名命名空间之外的值,我试图强制编译时constness将值放入内部链接空间,但显然我失败了。
最后,最后一个错误:
'“Literal”'不是类型'const char *'的有效模板参数,因为字符串文字永远不能在此上下文中使用
所以,令人惊讶的是(至少对我来说这是一个惊喜)字符串文字不能用作模板参数,但只要字符串(好吧,指向以空字符结尾的字符数组的指针)是一个编译器 - 时间值它可以用作非类型模板参数,因此:只要“它们是左值”,它们就可以在编译时使用(但它们是already lvalues!)。
我试图猜测为什么字符串文字永远不会在这个上下文中使用,我最好的猜测是两个具有相同内容的字符串文字不是相同的文字(因为指向内容的指针可以两个积分文字是相同的(它们是一个值,而不是一个指向值的指针)。
那么,这里的问题是什么?
namespace_const_message
和const_message
在编译时不可用,因此在print
模板函数中被禁止?感谢。
答案 0 :(得分:12)
模板的实例化变量需要具有外部
联系,const
隐含内部联系。所以你必须这样做
写:
extern char const constMessage[] = "Const message";
(另一种选择是将其作为静态类成员。 静态类成员始终具有外部链接。)
字符串文字的情况在某些方面类似:它们的类型是
char const[]
。但更糟糕的是:模板实例化(至少
早期的)需要一个名字,字符串文字没有名字。
更重要的是,它是否未指定相同的字符串文字
是否是相同的对象,所以在下面:
template <char const* m>
struct Toto { char const* f() const; };
Toto <"titi"> t1;
Toto <"titi"> t2;
未指明t1
和t2
是否具有相同的类型。
答案 1 :(得分:4)
来自c ++ 11标准§14.3.2.1
模板非类型参数
非类型非模板模板参数的模板参数应为以下之一:
- 表示整数或枚举类型的非类型模板参数, 转换的常量表达式(5.19)的类型 模板参数;或
- 非类型模板参数的名称;或
- 一个表示地址的常量表达式(5.19) 具有静态存储持续时间和外部或内部链接的对象 或具有外部或内部联系的功能,包括功能 模板和函数template-id但不包括非静态类 成员,表达(忽略括号)为&amp; id-expression,除了 那&amp;如果名称引用函数或数组,则可以省略 如果相应的模板参数是a,则应省略 参考;或
- 一个求值为空指针值的常量表达式(4.10);或
- 一个常量表达式,其值为null成员指针值(4.11);或
- 指向成员的指针,如5.3.1所述;或
- std :: nullptr_t类型的地址常量表达式。
醇>
问题:
为什么namespace_const_message和const_message在编译时不可用,因此禁止在打印模板函数中使用?
这就是constexpr
存在的原因。它们可以在需要的地方使用编译时评估,因此可用作模板参数。
我对字符串文字的猜测是否正确?
在争论之后有一个关于此的说明:
注意:字符串文字(2.14.5)不满足任何这些类别的要求,因此不符合 一个可接受的模板参数。