GCC vs Clang & MSVC Bug while using non type template parameter

285 views Asked by At

I was writing a rather simple example to clarify(better understand) the concept of non type template parameters. The example is shown below. To my surprise, the given example compiles with MSVC and Clang but fails to compile with GCC(for both C++14 & C++17).

template<typename T, T a>
void f()
{
      decltype(a) p; //is this valid given the call expression f<const int, 0>() below
}
int main()
{
     f<const int,0>();
}

Demo

Which compiler is right here according to the standard.

2

There are 2 answers

0
user12002570 On

To my current understanding of templates, this is what should happen:

Point 1

From temp.param#5:

The top-level cv-qualifiers on the template-parameter are ignored when determining its type.

On the first glance it seems that when applied to the given snippet, a should be int instead of const int.

But note the emphasis on the highlighted part in the above quoted statement. In particular, my interpretation of this(the phrase "when determining") is that the top-level cv-qualifiers are dropped when deducing the template parameter and so not when the template arguments are explicitly specified.

And since in my given snippet, we are explicitly specifying the template argument, there is no template argument deduction(TAD) here. Thus, the dropping of the top-level cv-qualifiers due to the above quoted temp.param#5 does not happen. Meaning the type of a is const int instead of int. This would suggest that GCC is correct in rejecting the code since we should provide an initializer for the const variable named p.

Note that i may be wrong in interpreting [temp.param#5] above.


Point 2

But from temp.param#6:

A non-type non-reference template-parameter is a prvalue. It shall not be assigned to or in any other way have its value changed.

Moreover, from expr#6:

If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

When applying temp.param#6 and expr#6 to the given snippet, it means that a(a prvalue) will finally be of type int instead of const int. That is, this suggest that MSVC and Clang are correct in accepting the code, since p is a non-const int.

So, given the above analysis, my thinking is that the code is valid and so msvc and clang are correct in accepting it.

0
ixSci On

I believe the temp.param#5 still applies here and nothing else is needed. Let's rewrite your example to make it easier to reason about:

template<typename T>
struct Foo
{
    template<T a>
    static void f()
    {
          decltype(a) p;
    }
};

int main()
{
     Foo<const int>::f<0>();
}

So we have a template Foo and a function template f() as 2 separate entities with separated template headers. Now if we instantiate Foo<const int> we get the following non-template:

struct Foo
{
    template<const int a>
    static void f()
    {
          decltype(a) p;
    }
};

And f() is still a function template and according to the cited standard reference decltype(a) should produce int.

You might argue that your example is different and while it is different in tokens it is not different in meaning. The non-type parameter of the original f is not deducible (doesn't have auto anywhere) it is pretty clearly specified to be of type T. It just so happens that T is something not available at the moment so until T is somehow specified, non-type parameter a makes no sense, it doesn't exist. Once you specified T it became a non-type parameter and we can apply whatever we should apply to it being an NTP.