2015-03-02 65 views
13

假设我们有这样的const char *非类型参数的模板函数:一些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,如果没有-t常量,则不需要?

匿名命名空间以外的值也是如此,我试图强制编译时常量将值放入内部链接空间,但显然我失败了。

最后,最后一个错误:

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

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

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

那么,这里有什么问题?

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

谢谢。

回答

9

模板的实例化变量需要具有外部 链接,而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

约联动伟大的答案!好一个。我想问你一些关于联系问题的更多信息; AFAIK一个未命名的命名空间强制内部链接,你说*模板的实例化变量需要外部链接*所以,为什么'namespace_message'和'namespace_constexpr_message'在模板中被接受?你能(请)延长你的答案吗? – 2015-03-02 14:53:43

+1

@PaperBirdMaster:这对于未命名的命名空间并非如此。它在C++ 11的脚注94中有所澄清:“尽管未命名的命名空间中的实体可能具有外部链接,但它们的名称通过其翻译单元唯一的名称进行有效限定,因此无法从其他任何翻译单元看到。”_ The规则在3.5/3和3.5/4中给出。 – 2015-03-02 16:19:58

+0

@LightnessRacesinOrbit好吧,所以我错误地提到了未命名的命名空间:它们不强制内部链接,它们可以有内部链接。感谢您的澄清。 – 2015-03-03 08:17:06

3

从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.