I've read some articles about this new C++11 feature but I didn't understand all stuff (I'm new to C++). How do I access a specific argument like I can do using va_arg
from stdarg.h
in C?
template <typename ... Args>
void f(Args ... args)
{
for(size_t i = 0; i < sizeof ...(args); i++)
{
// obviously, args...[i] didn't work...
}
}
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<class FirstType, typename...Args>
void f(FirstType&& first, Args&&...rest) {
//do loop body
loop_body(std::forward<FirstType>(first)...)
//do next "iteration"
f(std::forward<Args>(rest)...);
}
There's also this way to do it without recursion, but it's a bit more advanced:
template<typename...Args>
void f(Args&&...args) {
typedef int[] for_each;
for_each{((void)( loop_body(std::forward<Args>(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<i>(std::tie(std::forward<Args>(args)...));
typedef int[]
code is very strange, and so I'll lay it out here.loop_body(std::forward<Args>(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>(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
.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!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<typename...Args>
void f(Args&&...args) {
FOREACH_VARIADIC(loop_body(std::forward<Args>(args)));
}
Accepted answer is very good, but here's an idea using C++14 generic lambdas:
template <typename F>
void variadic_for_each(F) {}
template <typename F, typename Head, typename... Tail>
void variadic_for_each(Head&& head, Tail&&... tail, F f)
{
f(std::forward<Head>(head));
variadic_for_each(std::forward<Tail>(tail)..., f);
}
Example usage:
template <typename... Ts>
void myFunc(Ts&&... vs)
{
variadic_for_each(std::forward<Ts>(vs)..., [](auto&& v)
{
// loop body
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With