The problem is with TYPE var = args[c];
, what do you write for TYPE
? Each i
has a different type, so you won't be able to use a for
loop like this.
Generally, the normal approach is to use recursion.
void f() { } //end loop
template
void f(FirstType&& first, Args&&...rest) {
//do loop body
loop_body(std::forward(first)...)
//do next "iteration"
f(std::forward(rest)...);
}
There's also this way to do it without recursion, but it's a bit more advanced:
template
void f(Args&&...args) {
typedef int[] for_each;
for_each{((void)( loop_body(std::forward(args)) ),0)...,0};
}
And finally, if you really want to access one by index:
//note that "i" must be a compile time constant
auto var = std::get(std::tie(std::forward(args)...));
The
typedef int[]
code is very strange, and so I'll lay it out here.
We'd like to call a function
loop_body(std::forward(args))...;
but unfortunately, parameter packs can only be expanded in certain contexts, and that's not one of them. The easiest and most obvious solution is to pass the results of all those calls to a function that does nothing:
do_nothing(loop_body(std::forward(args))...)
, but unfortunately, this fails for
void
return types, because you can't instantiate a
void
to pass to the
do_nothing
. Worse, it could call each of the functions in the wrong order. One way to "convert" a
void
expression to something else is with an arcane trick with the comma operator,
(func(), 0)
executaes
func
and then "returns"
0
.
Worse, for reasons that I admittedly don't understand,
f(vs)...,0;
nor
(f(vs),0)...,0;
are valid contexts for expanding parameter packs. However,
type array[] = {vs...}
is a valid context. So now we have a way to combine this context with expressions with return values:
int array[] = {(f(vs),0)...};
And it works! Mostly!
If the parameter pack has zero types (yes, that's valid. never forget it.), then this results in a compiler error. So we have to add one additional zero on the end so there is always at least one element:
int array[] = {(f(vs),0)..., 0};
. Also, most compilers warn that
array
is an unused variable. One way to bypass that warning is to make the type a temporary.
int a = (expr);
is a local, but
(int)(expr)
creates an unnamed temporary. So we want
(int []){(f(vs),0)..., 0};
. For reasons I can't recall, this
(int[])
normally hidden behind a typedef. As a final detail, since some classes can overload the comma operator, it's safest to cast the functions to
void
:
int array[] = {((void)(f(vs)),0)..., 0};
Unrelated, I've contemplated this macro in the past, which hides the ugly details a little more. But I feel like there's a downside I'm overlooking or else it would be more common.
#define FOREACH_VARIADIC(EXPR) (int[]){((void)(EXPR),0)...,0}
template
void f(Args&&...args) {
FOREACH_VARIADIC(loop_body(std::forward(args)));
}