Given the following helper functions
template <typename ... Ts>
auto f(Ts&& ... args) {}
template <typename T>
auto g(T x) { return x; }
1) We expand a template parameter pack as usual.
template <typename ... Ts>
void test1(Ts&& ... args)
{
f(args...);
}
2) Here the expansion ...
occurs after the function call of g()
. This is also reasonable, since g()
is being called with each args
:
template <typename ... Ts>
void test2(Ts&& ... args)
{
f(g(args)...);
}
3) With the same logic I would expect test3(Is, args...)...
, but no. You have to write test3(Is..., args...)
:
template <typename ... Ts>
void test3(size_t i, Ts&& ... args)
{
f(args...);
}
template <typename ... Ts>
void test3(std::index_sequence<Is...>, Ts&& ... args)
{
// would expect test3(Is, args...)...;
test3(Is..., args...);
}
I know it, I use it, but well, I don't get it. The whole concept of template expansion is a form of expression folding. Not in the C++17 way, but in the sense that the subexpression before the ...
is being folded (or repeated if you like) in respect to the variadic parameter. In the test3
case we are "folding" the expression test3(Is, args...)
in respect to Is
. Yet we have to write test3(Is..., args...)
instead of test3(Is, args...)...
.
With this weird logic of the standard you could also write f(g(args...))
instead of f(g(args)...)
- however that's invalid. It looks like the language uses different logic in different contexts.
What is the rationale behind the different syntax?