【问题标题】:How does this variable argument notation work?这个变量参数符号是如何工作的?
【发布时间】:2014-01-21 16:24:58
【问题描述】:

在 C++ 中,我可以定义一个具有可变数量参数的函数,如下所示:

void a(int a...) {
    std::cout << a << std::endl;
}

然后这样称呼它:

a(100, 200, 300);

但是,显然我只能访问第一个参数:调用的输出是100

如何使用此表示法访问其他参数?

【问题讨论】:

  • man va_list 作为起点。但是如果你想在 C++11 中做这样的事情,忘记上面的内容,而是查找 variadic templates
  • 您熟悉va_list 或C 或C++ 中的可变参数函数吗?您可能想搜索va_list
  • 我什至不知道您可以使用 C 变量指定类型。在 C99 中看不到任何支持此功能的证据,但 GCC 4.8 编译它。
  • @LightnessRacesinOrbit:只有初始的非可变参数(其中必须至少有一个)指定了它们的类型。其他的是使用va_arg 宏的访问,这将任何类型检查留给程序员。
  • 啊,这就是我想知道的。 Why is int a... allowed?

标签: c++ arguments variadic-functions


【解决方案1】:

你的语法是......不幸的是,它指的是 C 风格的可变参数函数。

在 C++11 中,您应该更喜欢可变参数 templates。最简单的方法是这样的:

首先,一些帮助代码:

#include <utility>
template<typename Lambda>
void for_each_arg( Lambda&& unused ) {}

template<typename Lambda, typename Arg1, typename... Args>
void for_each_arg( Lambda&& closure, Arg1&& arg1, Args&&... args ) {
  closure( std::forward<Arg1>(arg1) );
  for_each_arg( std::forward<Lambda>(closure), std::forward<Args>(args)... );
}

现在,我们使用它:

#include <iostream>
template<typename... Args>
void foo( Args&&... args ) {
  for_each_arg( [](int x){
    std::cout << x << "\n";
  }, std::forward<Args>(args)... );
}
int main() {
  foo( 1, 2, 3 );
}

我们可以访问每个参数,并确保它们转换为int。请注意,到int 的转换会延迟到调用for_each_arg 的主体。

【讨论】:

    【解决方案2】:

    如果您使用 var args 接口,您需要能够从命名参数中得知总共提供了多少个参数。例如,&lt;stdio.h&gt; 函数通过将格式字符串作为最后一个命名的参数,后跟参数列表中指定的数量的参数来做到这一点。要访问参数,您需要使用各种 va_... 函数和类型。

    使用可变参数模板会更好:

    template <typename... T>
    void f(T... a) {
        // just expand the parameter pack over here
    }
    

    【讨论】:

    • 如果您要详细说明如何“扩展参数包”,此答案将非常有用
    • @Bathsheba:在最简单的情况下,您会使用g(a...),但仅此一项并没有太大帮助,描述如何完全做到这一点相当复杂!可能最简单的方法是使用auto t(std::tie(a...));,然后使用std::get&lt;...&gt;(t) 访问t
    • 是的,我认为你是对的,更详细的内容可能超出了答案范围。所以+1。
    【解决方案3】:

    使用您的函数a的示例代码:

    #include <iostream>
    #include <cstdarg>
    
    void a(int a...)
    {
        va_list args;
        va_start(args, a);
        int b = va_arg(args, int);
        int c = va_arg(args, int);
    
        std::cout << a << ", " << b << ", " << c << std::endl;
    }
    
    int main()
    {
        a(100, 200, 300);
        return 0;
    }
    

    可变参数语法不知道参数的数量或类型。因此,参数列表中的某些内容必须指示参数的数量和可能的类型。有几种方法通常用于确定参数的数量:

    1. 第一个参数是参数个数。
    2. 例如,最后一个参数是分隔符、NULL 或其他唯一值。
    3. 第一个参数具有确定参数数量、顺序和类型的其他嵌入信息,例如 printf 的格式参数。

    在这个例子中,我只是假设有三个参数。使用或多或少的参数调用 a 会导致未定义的行为(读取、随机结果、崩溃)。

    【讨论】:

      【解决方案4】:

      另一种可变参数解决方案:

      template<typename T, typename... Vs>
      void print(T&& t, Vs&&... vs) {
        std::cout << std::forward<T>(t) << std::endl;
        int sink[] { (std::cout << " " << std::forward<Vs>(vs) << std::endl, 0)... };
        (void)sink; // silence "unused variable" warning                                        
      }
      

      它的好处是不需要助手。我们使用包扩展将每个参数一次转发到 cout。出于语法原因,我利用逗号运算符使表达式 (cout..stuff.., 0) 解析为一个整数,然后我们将其丢弃到一个数组中;这让我们可以在复杂语句周围使用包扩展运算符。

      【讨论】:

        【解决方案5】:

        强制类型 int 的解决方案。
        但用法有点不同

        #include <initializer_list>
        #include <iostream>
        
        // Or you may use std::vector
        void print(const std::initializer_list<int>& a) {
            for (auto elem : a) {
                std::cout << elem << std::endl;
            }
        }
        
        int main(int argc, char *argv[])
        {
            print({1, 2, 3}); // extra braces.
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2015-02-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-25
          • 2020-01-09
          • 2019-09-01
          • 1970-01-01
          • 2011-05-30
          相关资源
          最近更新 更多