标准将char数组作为模板参数怎么说?

时间:2019-07-12 14:29:35

标签: c++ templates language-lawyer c++17 linkage

在研究this question的过程中,我发现(以前不知道)gcc和clang允许char数组作为模板参数,如果声明为static。例如。这段代码使用gcc和clang编译:

#include <type_traits>

template <int N, const char (&string)[N]>
auto foo()
{
    if constexpr (string[0] == 'i')
        return 0;
    else
        return 3.14f;
}

void bar()
{
    static constexpr char string1[] = "int";
    static constexpr char string2[] = "float";

    auto i = foo<sizeof(string1), string1>();
    auto f = foo<sizeof(string2), string2>();

    static_assert(std::is_same_v<decltype(i), int>);
    static_assert(std::is_same_v<decltype(f), float>);
}

MSVC也允许这样做。但是,要使其与MSVC一起使用,我必须在全局名称空间中声明两个字符串。然后它也可以正常工作。

所以我的问题是:标准对此有何表述?哪个编译器(如果有)是正确的?

1 个答案:

答案 0 :(得分:11)

这是从C ++ 14到C ++ 17的更改,看起来MSVS尚未赶上。先前在[temp.arg.nontype]中,非类型参数必须为

  

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

     
      
  • 对于整数或枚举类型的非类型模板参数,为该模板参数类型的转换后的常量表达式([expr.const]);或

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

  •   
  • 一个常量表达式([expr.const]),它指定具有静态存储持续时间和外部或内部链接的完整对象的地址,或者具有外部或内部链接的函数,包括函数模板和函数模板ID,但不包括非静态类成员,表示为&id-expression(忽略括号),其中id-expression是对象或函数的名称,但如果名称引用函数或数组,如果相应的模板参数是引用,则应省略;或

  •   
  • 一个常量表达式,其结果为空指针值([conv.ptr]);或

  •   
  • 一个常量表达式,其值为空成员指针值([conv.mem]);或

  •   
  • 指向成员的指针,如[expr.unary.op]中所述;或

  •   
  • 类型为std::nullptr_t的常量表达式。

  •   

强调我的

并且由于项目符号3,您不能使用块范围变量,因为每个[basic.link]/10的块范围变量都没有链接

  

这些规则未涵盖的名称没有链接。此外,除非另有说明,否则在块范围内声明的名称没有链接。

在C ++ 17中,此更改。 [temp.arg.nontype]现在有

  

非类型模板参数的模板参数应为模板参数类型的转换常量表达式。对于引用或指针类型的非类型模板参数,常量表达式的值不得引用(或对于指针类型,不得为其地址):

     
      
  • 子对象

  •   
  • 一个临时对象

  •   
  • 字符串文字

  •   
  • Typeid表达式的结果,或

  •   
  • 预定义的 ­ ­func _­_­变量。

  •   

现在,您可以使用块范围静态变量