C ++中函数的可变参数数量

时间:2009-10-16 18:43:56

标签: c++ c syntax variadic-functions

如何在C ++中的函数中使用可变数量的参数。

C#中的模拟:

public void Foo(params int[] a) {
    for (int i = 0; i < a.Length; i++)
        Console.WriteLine(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(1, 2);
}

Analog in Java:

public void Foo(int... a) {
    for (int i = 0; i < a.length; i++)
        System.out.println(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(2);
}

8 个答案:

答案 0 :(得分:42)

这些被称为Variadic functions。维基百科列出example code for C++

  

可移植地实现可变参数   C编程中的函数   language,标准stdarg.h标题   应该使用文件。年纪大了   varargs.h标头已被弃用   赞成stdarg.h。 在C ++中,   应使用头文件cstdarg

     

要创建可变参数函数,   省略号(...)必须放在   参数列表的结尾。在 - 的里面   函数体,变量   必须定义类型va_list。那么   宏va_start(va_list, last fixed param)va_arg(va_list, cast type),   可以使用va_end(va_list)。对于   例如:

#include <stdarg.h>

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}

答案 1 :(得分:14)

真正的C ++解决方案是可变参数模板。您需要一个相当新的编译器,并在需要时启用C ++ 11支持。

两种方法来处理&#34;对所有函数参数执行相同的操作&#34;问题:递归地,并且有一个丑陋的(但非常符合标准的)解决方案。

recursive solution看起来有点像这样:

template<typename... ArgTypes>
void print(ArgTypes... args);
template<typename T, typename... ArgTypes>
void print(T t, ArgTypes... args)
{
  std::cout << t;
  print(args...);
}
template<> void print() {} // end recursion

它为每个参数集合生成一个符号,然后为递归的每一步生成一个符号。这至少可以说是次优的,所以the awesome C++ people here at SO想到了a great trick abusing the side effect of a list initialization

struct expand_type {
  template<typename... T>
  expand_type(T&&...) {}
};
template<typename... ArgTypes>
void print(ArgTypes... args)
{ 
  expand_type{ 0, (std::cout << args, 0)... };
}

代码不是针对一百万个略有不同的模板实例生成的,作为奖励,您可以获得函数参数的保留顺序。有关此解决方案的详细信息,请参阅其他答案。

答案 2 :(得分:5)

除了其他答案之外,如果你只是想传递一个整数数组,为什么不呢:

void func(const std::vector<int>& p)
{
    // ...
}

std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);

func(params);

但是你不能在参数,表格中调用它。您必须使用答案中列出的任何可变参数函数。 C ++ 0x将允许可变参数模板,这将使其类型安全,但现在它基本上是内存和转换。

您可以模拟某种可变参数 - &gt;向量事物:

// would also want to allow specifying the allocator, for completeness
template <typename T> 
std::vector<T> gen_vec(void)
{
    std::vector<T> result(0);
    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1)
{
    std::vector<T> result(1);

    result.push_back(a1);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2)
{
    std::vector<T> result(1);

    result.push_back(a1);
    result.push_back(a2);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2, T a3)
{
    std::vector<T> result(1);

    result.push_back(a1);
    result.push_back(a2);
    result.push_back(a3);

    return result;
}

// and so on, boost stops at nine by default for their variadic templates

用法:

func(gen_vec(1,2,3));

答案 3 :(得分:5)

在C ++ 11及更高版本中,您还可以使用初始化列表。

int sum(const initializer_list<int> &il)
{
    int nSum = 0;
    for (auto x: il) 
        nSum += x;
    return nsum;
}

cout << sum( { 3, 4, 6, 9 } );

答案 4 :(得分:3)

请参阅Variadic functions in C, Objective-C, C++, and D

您需要添加stdarg.h,然后使用va_listva_startva_argva_end,如维基百科文章中的示例所示。它比Java或C#更麻烦,因为C和C ++对varargs的内置支持有限。

答案 5 :(得分:1)

如果您不关心可移植性,可以使用this C99 codegcc's statement expressions移植到C ++:

#include <cstdio>

int _sum(size_t count, int values[])
{
    int s = 0;
    while(count--) s += values[count];
    return s;
}

#define sum(...) ({ \
    int _sum_args[] = { __VA_ARGS__ }; \
    _sum(sizeof _sum_args / sizeof *_sum_args, _sum_args); \
})

int main(void)
{
    std::printf("%i", sum(1, 2, 3));
}

您可以使用C ++ 0x'slambda表达式执行类似操作,但我正在使用的gcc版本(4.4.0)不支持它们。

答案 6 :(得分:1)

GManNickG和Christoph的答案很好,但是可变参数功能允许你按照你想要的方式输入 ... 参数,而不仅仅是整数。如果 希望将来,将不同类型的许多变量和值推送到函数中而不使用可变参数函数,因为它也是对您来说很难或太复杂,或者您不喜欢使用它的方式,或者您不想包含使用它所需的标题,那么您始终可以使用 void** 参数。

例如,Stephan202发布了:

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}

这也可以写成:

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
       tot+=*(double*)params[j];
    return tot/count;
}

现在就像这样使用它:

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
}

完整代码:

#include "stdafx"
#include <process.h>

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
        tot+=*(double*)params[j];
    return tot/count;
}

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
 }

<强>输出:

  

平均值是:2

     

按任意键继续。 。

答案 7 :(得分:1)

我在c ++ builder xe.xx中是这样的:

String s[] = {"hello ", " unli", " param", " test"};
String ret = BuildList(s, 4);

String BuildList(String s[], int count)
{
    for(int i = 0; i < count; i++)
    {
        //.... loop here up to last s[i] item ... 
    }
}