一些const char *在编译时不可用?

时间:2015-03-02 12:25:09

标签: c++ string templates compile-time string-literals

假设我们有一个非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_messagenamespace_message时不能用于常量表达式;如果我必须赌他们其中一个不能用于常数表达式,我会打赌没有常数,但是那个已经作为常量表达式的那个!

  

注意:'{anonymous} :: namespace_const_message'未被声明为'constexpr'

namespace_message既未声明为constexpr,也用于常量表达式,其值在编译时推导出来。如果表达式为constexpr,为什么需要const,如果不是const,则为什么不需要?

同样适用于匿名命名空间之外的值,我试图强制编译时constness将值放入内部链接空间,但显然我失败了。

最后,最后一个错误:

  

'“Literal”'不是类型'const char *'的有效模板参数,因为字符串文字永远不能在此上下文中使用

所以,令人惊讶的是(至少对我来说这是一个惊喜)字符串文字不能用作模板参数,但只要字符串(好吧,指向以空字符结尾的字符数组的指针)是一个编译器 - 时间值它可以用作非类型模板参数,因此:只要“它们是左值”,它们就可以在编译时使用(但它们是already lvalues!)。

我试图猜测为什么字符串文字永远不会在这个上下文中使用,我最好的猜测是两个具有相同内容的字符串文字不是相同的文字(因为指向内容的指针可以两个积分文字是相同的(它们是一个值,而不是一个指向值的指针)。

那么,这里的问题是什么?

  • 为什么namespace_const_messageconst_message在编译时不可用,因此在print模板函数中被禁止?
  • 我对字符串文字的猜测是否正确?

感谢。

2 个答案:

答案 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;

未指明t1t2是否具有相同的类型。

答案 1 :(得分:4)

来自c ++ 11标准§14.3.2.1

  

模板非类型参数

     

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

     
      
  1. 表示整数或枚举类型的非类型模板参数,   转换的常量表达式(5.19)的类型   模板参数;或
  2.   
  3. 非类型模板参数的名称;或
  4.   
  5. 一个表示地址的常量表达式(5.19)   具有静态存储持续时间和外部或内部链接的对象   或具有外部或内部联系的功能,包括功能   模板和函数template-id但不包括非静态类   成员,表达(忽略括号)为&amp; id-expression,除了   那&amp;如果名称引用函数或数组,则可以省略   如果相应的模板参数是a,则应省略   参考;或
  6.   
  7. 一个求值为空指针值的常量表达式(4.10);或
  8.   
  9. 一个常量表达式,其值为null成员指针值(4.11);或
  10.   
  11. 指向成员的指针,如5.3.1所述;或
  12.   
  13. std :: nullptr_t类型的地址常量表达式。
  14.   

问题:

为什么namespace_const_message和const_message在编译时不可用,因此禁止在打印模板函数中使用?

这就是constexpr存在的原因。它们可以在需要的地方使用编译时评估,因此可用作模板参数。

我对字符串文字的猜测是否正确?

在争论之后有一个关于此的说明:

  

注意:字符串文字(2.14.5)不满足任何这些类别的要求,因此不符合   一个可接受的模板参数。