What does the standard say about char arrays as te

2020-06-09 06:51发布

问题:

During my research for an answer for this question I found (I did not know that before) that gcc and clang allow char arrays to be template arguments if they are declared static. E.g. this code compiles with gcc and 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 also allows that. However, to make it work with MSVC, I have to declare the two strings in the global namespace. Then it works just as well.

So my question is: What does the standard say about this? Which compiler (if any) is right?


Update:

This issue has been fixed in VS 2019 version 16.4 (msvc v19.24): https://developercommunity.visualstudio.com/content/problem/341639/very-fragile-ice.html

回答1:

This is a change from C++14 to C++17 that looks like MSVS hasn't caught up with. Previously in [temp.arg.nontype] a non type argument had to be

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.

emphasis mine

and because of bullet 3 you could not use a block scope variable as block scope variables have no linkage per [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.

In C++17 this changes. [temp.arg.nontype] now has

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.

This now allows you to use a block scope static variable