I am confused by the error output of GCC for the partial specializations below.
// Primary
template<class T, class U1, class U2, class... Us>
struct S{};
// #1
template<class T, class... Us>
struct S<T, T, T, Us...>{};
// #2
template<class T, class U, class... Us>
struct S<T, T, U, Us...>{};
// #3
template<class T, class U, class... Us>
struct S<T, U, T, Us...>{};
// #4
template<class T, class U1, class U2, class U3, class... Us>
struct S<T, U1, U2, U3, Us...>{};
When I tried to call S<int, int, long, float, double>
the compiler said it could not decide which of #2 and #4 to choose as the one instantiate. But I think #2 is more specialized than #4 regarding this, so it should make a decision.
My reasons are listed below:
When both #2 and #4 could be called, i.e the number of template arguments is no less than 4, from the way introduced here, we can define a fictitious function for #2 which is
template<class T, class U, class... Us>
f<X<T, T, U, Us...>); // #A
and one for #4
template<class T, class U1, class U2, class U3, class... Us>
f<X<T, U1, U2, U3, Us...>); // #B
Then, #A from #B (void(X<T, T, U, aUs...>)
from void(X<U1, U2, U3, U4, bUs...>)
):
P1=T, A1=U1: T=U1
,P2=T, A2=U2: T=U2: fails
;
and #B from #A (void(X<T, U1, U2, U3, bUs...>)
from void(X<U1, U2, U3, aUs...>)
):
P1=T, A1=U1
,P2=U1, A2=U2
,P3=U2, A3=U3
,P4=U3, A4=<aUs...>[0]
(<aUs...>
is not empty in this case),P5=Us..., A5=<aUs...>[1,]
(<aUs...>[1,]
may be empty now).
So #B from #A is successfully performed in this way, then I think #2 is more specialized than #4 so #2 should be chosen.
If GCC is right (most possible), I would like to know which part of my above statements goes wrong. Thanks very much.
A live example could be found here.
CodePudding user response:
Conceptually neither #2
nor #4
is more specialized, because there are lists of template arguments for A
such that #2
is viable, but not #4
, as well as such sets for which #4
is viable, but #2
is not.
In your formal analysis everything looks correct, except that at
P4=U3, A4=<aUs...>[0]
(<aUs...>
is not empty in this case),
deduction fails, because you need to actually compare all of <aUs...>
as a single (imagined) template argument A4
to be matched against P4
and per [temp.deduct.type]#9.2, because it originated from a pack expansion, A4
should then have either no corresponding element in the template argument list of the parameter or correspond to an element of the parameter's template argument list that also originated from a pack expansion, which P4
does not (since it is just U3
).