【问题标题】:What's va_arg() in C++11 variadic template? [duplicate]C++11 可变参数模板中的 va_arg() 是什么? [复制]
【发布时间】:2014-05-28 23:10:13
【问题描述】:

我已经阅读了一些关于这个 C++11 新特性的文章,但我并不了解所有内容(我是 C++ 新手)。如何访问特定参数,就像在 C 中使用 stdarg.h 中的 va_arg 一样?

template <typename ... Args>
void f(Args ... args)
{
    for(size_t i = 0; i < sizeof ...(args); i++)
    {
        // obviously, args...[i] didn't work...
    }
}

【问题讨论】:

  • 如果所有参数都具有相同的类型(T),我认为这个答案有最好的解决方案:stackoverflow.com/a/3703826/364818 基本上,在f() 的第一行创建一个本地std::vector&lt;T&gt; x = { { args } }; 和然后你可以遍历x[]的元素

标签: c++ templates c++11 clang variadic-templates


【解决方案1】:

问题出在TYPE var = args[c];,你为TYPE写什么?每个i 都有不同的类型,所以你不能像这样使用for 循环。

一般来说,正常的做法是使用递归。

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

也有这种不用递归的方法,但是更高级一点:

template<typename...Args> 
void f(Args&&...args) {  
    typedef int[] for_each;
    for_each{((void)(   loop_body(std::forward<Args>(args))  ),0)...,0};
}

最后,如果你真的想通过索引访问一个:

//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&lt;Args&gt;(args))...;,但不幸的是,参数包只能在某些上下文中扩展,而这不是其中之一。最简单和最明显的解决方案是将所有这些调用的结果传递给一个什么都不做的函数:do_nothing(loop_body(std::forward&lt;Args&gt;(args))...),但不幸的是,这对于 void 返回类型失败,因为您无法实例化 void 以传递到do_nothing。更糟糕的是,它可能会以错误的顺序调用每个函数。将void 表达式“转换”为其他表达式的一种方法是使用逗号运算符(func(), 0) 执行func 然后“返回”0
更糟糕的是,出于我承认不理解的原因,f(vs)...,0;(f(vs),0)...,0; 是扩展参数包的有效上下文。但是,type array[] = {vs...} 是一个有效的上下文。所以现在我们有一种方法可以将此上下文与具有返回值的表达式结合起来:int array[] = {(f(vs),0)...}; 它可以工作!主要是!
如果参数包的类型为零(是的,这是有效的。永远不要忘记它。),那么这会导致编译器错误。所以我们必须在最后添加一个额外的零,所以总是至少有一个元素:int array[] = {(f(vs),0)..., 0};。此外,大多数编译器警告array 是一个未使用的变量。绕过该警告的一种方法是将类型设为临时类型。 int a = (expr); 是本地的,但 (int)(expr) 创建一个未命名的临时。所以我们想要(int []){(f(vs),0)..., 0};。由于我不记得的原因,这个(int[]) 通常隐藏在 typedef 后面。最后一点,由于某些类可以重载逗号运算符,因此将函数强制转换为 void 是最安全的:int array[] = {((void)(f(vs)),0)..., 0};

不相关,我过去考虑过这个宏,它隐藏了更多丑陋的细节。但我觉得我忽略了一个缺点,否则它会更常见。

#define FOREACH_VARIADIC(EXPR) (int[]){((void)(EXPR),0)...,0}    

template<typename...Args> 
void f(Args&&...args) {  
    FOREACH_VARIADIC(loop_body(std::forward<Args>(args)));
}

【讨论】:

  • 您能再解释一下typedef char[] for_each 方法吗?
  • 您应该在您的for_each 中添加一些void() 以打破可能的过载operator,。或for_each x{ (void)(pattern)..., 0}。 declarator-id 是必需的,顺便说一句。
  • 仅出于副作用将包扩展传递给函数是危险的,因为评估是无序的。
  • (declarator-id 可能是不必要的,因为这不是一个声明,而是一个有效的表达式;一种功能转换符号的形式。)
  • 我不推荐这个宏,因为它使用非 C++ C99 复合文字,而使用别名模板制作这个可移植的 C++ 非常容易。 @dyp 这不是功能转换符号,因为它与语法 (type)expression 不匹配。 ——约翰内斯·绍布
【解决方案2】:

已接受的答案非常好,但这里有一个使用 C++14 通用 lambda 的想法:

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);
}

示例用法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 2012-11-29
    相关资源
    最近更新 更多