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:
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 ofx
in (2) and a unique value of that type forx
itself, substitute thisx
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:
In
void f(A<x>)
what exactly are we substituting forx
? A unique value. But what are the characteristics of that value? Is it an id-expression (resulting indecltype(auto)
deducing its declared type) or not? If it's not an id-expression, then is it an lvalue or a prvalue? That affects whetherdecltype(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.