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

Some const char * are unavailable at compile time?

假设我们有一个非类型参数为 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;
}

以上代码产生的错误如下:

the value of '{anonymous}::namespace_const_message' is not usable in a constant expression

我不明白为什么 namespace_const_message 不能用于常量表达式而 namespace_message 可以;如果我必须打赌其中之一不能用在常量表达式中,我会打赌没有常量,但它已经可以用作常量表达式了!

note: '{anonymous}::namespace_const_message' was not declared 'constexpr'

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

同样适用于匿名名称之外的值space,我试图强制编译时常量将值放入内部链接space,但很明显我已经失败。

最后,最后一个错误:

'"Literal"' is not a valid template argument for type 'const char*' because string literals can never be used in this context

所以,令人惊讶的是(至少这对我来说是个惊喜)字符串文字不能用作模板参数,但只要字符串(好吧,指向以 null 结尾的字符数组的指针)是编译时值它可以用作非类型模板参数,因此:它们在编译时可用,只要 "they are a lvalue"(但它们是 already lvalues!)。

我试图猜测为什么在这种情况下永远不能使用字符串文字,我最好的猜测是具有相同内容的两个字符串文字不是相同的文字(因为指向内容可能不同)而两个整数文字是相同的(它们是一个值,而不是指向值的指针)。

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

谢谢。

模板的实例化变量需要有外部 链接,const 是隐式内部链接。所以你必须 写:

extern char const constMessage[] = "Const message";

(另一种选择是将其设为静态 class 成员。 静态 class 成员总是有外部链接。)

字符串文字的情况在某些方面是相似的:它们的类型是 char const[]。但更糟糕的是:模板实例化(至少 早期的)需要一个名字,而字符串文字没有。 更重要的是,未指定是否相同的字符串文字 是否是同一个对象,所以在下面:

template <char const* m>
struct Toto { char const* f() const; };

Toto <"titi"> t1;
Toto <"titi"> t2;

t1t2 是否为同一类型是不确定的。

来自 c++11 标准 §14.3.2.1

Template non-type arguments

A template-argument for a non-type, non-template template-parameter shall be one of:

  1. for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
  2. the name of a non-type template-parameter; or
  3. a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  4. a constant expression that evaluates to a null pointer value (4.10); or
  5. a constant expression that evaluates to a null member pointer value (4.11); or
  6. a pointer to member expressed as described in 5.3.1; or
  7. an address constant expression of type std::nullptr_t.

你的问题:

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

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

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

在参数之后有一个注释:

Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.