In the next sample program, a class template with with non-type template parameter decltype(auto)
is partially specialized with non-type template parameter auto
:
template <decltype(auto)>
struct A { static const int v = 0; };
template <auto n>
struct A<n> { static const int v = 1; };
and it is accepted by all compilers, only GCC issues a warning
warning: partial specialization 'struct A<n>' is not more specialized than
note: primary template 'template<decltype(auto) <anonymous> > struct A'
Shall the following assertions be true (as they are in Clang)?
const int i = 0;
static_assert( A<i>::v == 1 ); //ok everywhere
static_assert( A<(i)>::v == 0 );//ok in Clang only
As far as I understand, here A<(i)>
in Clang selects the primary class template with the parameter of type const int&
, so A<(i)>::v == 0
. And A<i>
in Clang selects the specialization with the parameter of type int
, so A<i>::v == 1
.
But GCC and MSVC disagree with it, and they select the specialization in both cases, so A<(i)>::v == 1
. Online demo: https://godbolt.org/z/Wfd8e1KPz
Which compiler is correct here according to the standard?
We have to check whether the partial specialization actually is more specialized than the primary template, as required by [temp.spec.partial.general]/9.2. (As far as I know, Clang has not implemented this rule, so in effect, Clang fails to actually express any opinion on this question.)
[temp.spec.partial.order] explains how to perform the "more specialized" comparison between two partial specializations (we must treat the primary template as if it were also a partial specialization): we must rewrite them into function templates each of which takes the class template as its only parameter:
template <decltype(auto) x>
void f(A<x>); // (1)
template <auto x>
void f(A<x>); // (2)
Then we have to apply [temp.func.order] to order these two function templates. The basic rule explained in paragraphs 3–4 is that to check whether (2) is more specialized than (1), we have to synthesize a unique type for the auto
type of x
in (2) and a unique value of that type for x
itself, substitute this x
into the declaration (2), and attempt to deduce (1) from the resulting function type.
But we have a problem because it's not actually clear what (if anything) is produced by the substitution into (2). Let's read p3 very carefully:
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
In void f(A<x>)
what exactly are we substituting for x
? A unique value. But what are the characteristics of that value? Is it an id-expression (resulting in decltype(auto)
deducing its declared type) or not? If it's not an id-expression, then is it an lvalue or a prvalue? That affects whether decltype(auto)
is deduced as a reference type or a non-reference type. If it's deduced as a reference type, then additional restrictions apply, which might make the substituted template-id invalid. What happens then? The partial ordering rules don't say.
At this point I'm convinced that we have a serious problem with the specification of partial ordering in the standard that we are nowhere close to knowing how to solve. Sorry. My advice is to simply not specialize templates that have a decltype(auto)
template parameter.