gcc and clang disagree about whether the following code should compile:
template <typename... Args>
struct tuple {};
template <typename>
struct Test;
template <
typename... Types,
template <Types> typename... Outer, // XXX
Types... Inner
>
struct Test<tuple<Outer<Inner>...>> {};
template <long T> struct O1 {};
template <unsigned T> struct O2 {};
Test<tuple<O1<1>, O2<2>>> test;
clang accepts the code, deducing Types = {long, unsigned}, Outer = {O1, O2}, Inner={1L, 2U}
. Structurally, this seems correct.
gcc rejects the code with a deduction failure. Interestingly, it does accept if O2
is changed to take a long
non-type template parameter, which seems inconsistent to me. It suggests that Types can be expanded if Types = {long, long}
but not if Types = {long, unsigned}
.
However, it's not clear to me from the standard which compiler is correct. The core question is: on the line denoted XXX
, is it valid to have a parameter pack as the type of the template template parameter's non-type template parameter? Should it expand the way that clang claims it does?
It is not valid because:
a type parameter pack cannot be expanded in its own parameter clause.
As from [temp.param]/17:
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
So consider the following invalid example:
template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
The above example is invalid because the template type parameter pack Ts
cannot be expanded in its own parameter list.
For the same reason, your code example is invalid.