Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 387
sebrockm Avatar asked Jul 12 '19 14:07

sebrockm


People also ask

What is a template argument?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

What is the role of parameter in a template?

Similar to how standard function parameters may be used to send values to a function, template parameters allow you to pass types to a function as well. A template parameter is a specific form of the parameter that can be used to pass a type as an argument.


1 Answers

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

like image 173
NathanOliver Avatar answered Oct 11 '22 05:10

NathanOliver