什么是va_arg()在C ++ 11可变参数模板? [英] What's va_arg() in C++11 variadic template?

查看:194
本文介绍了什么是va_arg()在C ++ 11可变参数模板?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了一些关于这个新的C ++ 11功能的文章,但我不明白所有的东西(我是C ++的新手)。我如何访问一个特定的参数,如我可以使用 va_arg stdarg.h

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...
    }
}


推荐答案

问题是 TYPE var = args [c]; code> TYPE ?每个 i 都有不同的类型,所以你不能像这样使用循环循环。

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 [] 代码非常奇怪,因此我将它放在这里。

我们想调用一个函数 loop_body(std :: forward< Args> args))...; 但不幸的是,参数包只能在特定上下文中扩展,这不是其中之一。最简单和最明显的解决方案是将所有这些调用的结果传递给不执行任何操作的函数: do_nothing(loop_body(std :: forward< Args>(args))...),但不幸的是,这对于 void 返回类型失败,因为你不能实例化一个 void 传递给 do_nothing 。更糟的是,它可能以错误的顺序调用每个函数。将 void 表达式转换为其他形式的一种方法是使用逗号运算符(func(),0) executaes func ,然后返回 0

原因,我承认不明白, f(vs)...,0; 也不是(f(vs),0) ,0; 是扩展参数包的有效上下文。但是, type array [] = {vs ...} 是一个有效的上下文。所以现在我们有一个方法可以将这个上下文与具有返回值的表达式组合: int array [] = {(f(vs),0)...}; 作品!大多数情况下!

如果参数包有零类型(是,那是有效的,永远不会忘记它),那么会导致编译器错误。所以我们必须在最后添加一个额外的零,所以总是有至少一个元素: int array [] = {(f(vs),0)...,0}; 。此外,大多数编译器警告,数组是一个未使用的变量。绕过该警告的一种方法是使类型为临时类型。 int a =(expr); 是本地,但(int)(expr)创建一个未命名的临时。所以我们想要(int []){(f(vs),0)...,0}; 。由于原因,我不记得,这(int [])通常隐藏在typedef后面。作为最后一个细节,因为一些类可以重载逗号运算符,所以最安全的是将函数转换为 void int array [] = { void)(f(vs)),0)...,0};


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>(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.
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<typename...Args> 
void f(Args&&...args) {  
    FOREACH_VARIADIC(loop_body(std::forward<Args>(args)));
}

这篇关于什么是va_arg()在C ++ 11可变参数模板?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆