From [temp.variadic] (working draft) it seemed to me that a parameters pack can be expanded while defining an arguments list of another template class or function.
Consider the following class:
template<typename... T>
struct S {
template<T... I>
void m() {}
};
int main() {
S<int, char> s;
// ...
}
The intent is to capture the types used to specialize the template class S
and use them to define an arguments list of non-type parameters for the member method m
(T
is limited to a few types, of course, but this isn't the argument of the question).
Is this legal code? Can I use a parameter pack the way I used it or am I misinterpreting the standard (pretty sure that's the case indeed)?
In order to add more details to the question, here are some results from a few experiments with the major compilers:
s.m<0, 'c'>()
: clang v3.9 compiles it, GCC v6.2 and GCC v7 return an error.s.m<0>();
: clang v3.9 compiles it, GCC v6.2 returns an error and GCC v7 stops the compilation with an ICE.s.m<>();
: clang v3.9, GCC v6.2 and GCC v7 compile it with no errors.
At least, compilers seem to be as confused as me.
The definition of the template
S
, and the instantiation ofS<int, char>
, are valid.See [temp.param]/15: "A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded parameter packs is a pack expansion."
This means that
template<T ...I>
can mean one of two different things: ifT
is a non-pack type, then it declares a normal parameter pack, accepting any number ofT
s. However, ifT
contains an unexpanded parameter pack, then the parameter declaration is instead expanded into a sequence of parameters when the outer template is instantiated.Your first call to
m
is valid, but your second and third calls tom
are ill-formedThe instantiation of
S<int, char>
looks like this:(where
I$0
andI$1
are the first and second slices of the packI
).Therefore (because neither
I$0
norI$1
can be deduced from a call tom
),s.m<0,'c'>()
is valid buts.m<0>()
ands.m<>()
are ill-formed.