What is va_arg () in a C ++ 11 variation template?

I read several articles about this new C ++ 11 feature, but I didn't understand all the things (I'm new to C ++). How to access a specific argument, how can I 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... } } 
+6
source share
3 answers

The problem with TYPE var = args[c]; what do you write for type? Each i has a different type, so you cannot use a for loop like this.

Typically, the usual 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 is also this way to do this without recursion, but it is 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 by index:

 //note that "i" must be a compile time constant auto var = std::get<i>(std::tie(std::forward<Args>(args)...)); 


The typedef int[] code typedef int[] very strange, and so I will describe it here. We would like to call the function loop_body(std::forward<Args>(args))...; but, unfortunately, parameter packages can only be expanded in certain contexts, and this is not one of them. The simplest and most obvious solution is to pass the results of all these calls to a function that does nothing: do_nothing(loop_body(std::forward<Args>(args))...) , but unfortunately this is not suitable for return types void types because you cannot create an instance of void to pass to do_nothing . Worse, he could call each of the functions in the wrong order. One way to "convert" the void expression to something else is with a secret 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; and (f(vs),0)...,0; are valid contexts for expanding parameter packages. 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! Mainly!
If the parameter package has null types (yes, it really is, never forget it.), Then this leads to a compiler error. Therefore, we need to add one more zero to the end, therefore there is always at least one element: int array[] = {(f(vs),0)..., 0}; . In addition, most compilers warn that array is an unused variable. One way around this warning is to make the type temporary. int a = (expr); is local, but (int)(expr) creates an unnamed temporary. Therefore, we want (int []){(f(vs),0)..., 0}; . For reasons that I cannot remember, this (int[]) usually hidden behind a typedef. As a final detail, since some classes may overload the operator with a comma, it is safer to pass functions to void : int array[] = {((void)(f(vs)),0)..., 0};

Unconnected, I have considered this macro in the past, which hides ugly details a bit more. But I feel that there is a flaw that I miss, otherwise it will 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))); } 
+15
source

Clang supports lambda that work directly with package extension elements , unfortunately GCC cannot handle this yet

 struct E { E(...) {} } e{ ([&]{ /* use args here */ }(), 0)... }; 
+3
source

The accepted answer is very good, but here is the 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); } 

Usage example:

 template <typename... Ts> void myFunc(Ts&&... vs) { variadic_for_each(std::forward<Ts>(vs)..., [](auto&& v) { // loop body }); } 
+2
source

All Articles