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

seb*_*ckm 12 c++ templates linkage language-lawyer c++17

在研究此问题的答案的过程中,我发现(以前不知道)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>);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Nat*_*ica 12

这是从C ++ 14到C ++ 17的变化,似乎MSVS尚未赶上。以前在[temp.arg.nontype]中必须使用非类型参数

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

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

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

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

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

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

  • 指向[expr.unary.op]中所述的成员的指针;要么

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

重点矿

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

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

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

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

  • 一个子对象

  • 临时物体

  • 字符串文字,

  • Typeid表达式的结果,或者

  • 预定义的 func__变量。

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