Consider the following structure: S
is a class template with a variadic pack of template type parameters ...Ts
. The class contains a nested class template N
, which has a single template template parameter C
. C
itself is templated with a variadic pack of non-type template parameters that have exactly the types Ts...
.
template <typename ...Ts>
struct S
{
template <template <Ts...> typename C>
struct N
{
C<42> c;
};
};
GCC rejects the declaration of c
with:
error: expansion pattern '<anonymous>' contains no parameter packs
C<42> c;
^
I don't know what this diagnostic means, and I'm guessing this is a GCC bug.
Clang accepts this structure, and also accepts a reasonable instantiation such as:
template<int> struct W {};
S<int>::N<W> w; // ok, makes sense
Of course, the declaration C<42> c;
itself places constraints on the template used as an argument for the parameter C
, which will be required even if the Ts...
in C
were simply auto...
. e.g. the first argument of Ts...
must be a type that is implicitly convertible from an int
. Also, the remaining parameters in Ts...
, if any, must have defaults.
template<bool(*)()> struct X1 {};
S<int>::N<X1> x1; // error: value of type 'int' is not implicitly convertible to 'bool (*)()'
template<int, char> struct X2 {};
S<int>::N<X2> x2; // error: too few template arguments for class template 'X2'
Interestingly, there do appear to be constraints imposed by the relationship between ...Ts
and Ts...
. e.g. all the specified arguments for S
must now be valid non-type template parameter types:
template<int> struct Y {};
S<int, void>::N<Y> y; // error: a non-type template parameter cannot have type 'void'
On the other hand, Clang also accepts instantiations with seemingly incompatible arguments for S
, and non-type template parameters for C
:
template<int> struct Z {};
S<bool(*)(), bool>::N<Z> z; // ok ?? But why? both number and type of 'Ts' is different
Here's a demo.
So what are the rules for how ...Ts
and Ts...
constrain each other in this structure?
I came across this issue while trying to understand this question where it's indicated that MSVC doesn't accept the code as well.
Z
when named asC
. If yes, then checkingN
is harder; if not, then gcc is right. – DuvallN
isC<1> n;
thenb
is accepted as well, andX
has no default arguments. – DalhousieC<1>
case does not have that problem. – Duvall42
are also ill-formed, ndr -- that is known as the towel rule. (this is a joke, as conveying humour on the internet is difficult without being explicit) – Duvall