标准对 char 数组作为模板参数有何看法?

What does the standard say about char arrays as template arguments?

在研究 的答案时,我发现(我以前不知道)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 一起使用,我必须在全局命名空间中声明这两个字符串。然后效果一样。

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


更新:

此问题已在 VS 2019 版本 16.4 (msvc v19.24) 中修复:https://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html

这是从 C++14 到 C++17 的变化,看起来 MSVS 还没有跟上。以前在 [temp.arg.nontype] 中,非类型参数必须是

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

  • for a non-type template-parameter of integral or enumeration type, a converted constant expression ([expr.const]) of the type of the template-parameter; or

  • the name of a non-type template-parameter; or

  • a constant expression ([expr.const]) that designates the address of a complete 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, where the id-expression is the name of an object or function, 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

  • a constant expression that evaluates to a null pointer value ([conv.ptr]); or

  • a constant expression that evaluates to a null member pointer value ([conv.mem]); or

  • a pointer to member expressed as described in [expr.unary.op]; or

  • a constant expression of type std::nullptr_t.

强调我的

并且由于项目符号 3,您不能使用块作用域变量,因为块作用域变量没有每个 [basic.link]/10

的链接

Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope has no linkage.

在 C++17 中,这发生了变化。 [temp.arg.nontype] 现在有

A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject,

  • a temporary object,

  • a string literal,

  • the result of a typeid expression, or

  • a predefined ­­func_­_­ variable.

这现在允许您使用块作用域静态变量