36

如何在 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);
}

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

8 回答 8

50

这些被称为可变参数函数。维基百科列出了 C++ 的示例代码

为了在 C 编程语言中可移植地实现可变参数函数,应该使用标准的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;
}
于 2009-10-16T18:47:06.623 回答
20

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

处理“对所有函数参数做同样的事情”问题的两种方法:递归和使用丑陋(但非常符合标准)的解决方案。

递归解决方案看起来有点像这样:

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

它为每个参数集合生成一个符号,然后为递归的每一步生成一个符号。至少可以说这是次优的,所以这里很棒的 C++ 人想到了一个滥用列表初始化副作用的绝妙技巧

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

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

于 2014-12-14T16:47:46.530 回答
13

在 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 } );
于 2016-11-01T08:31:15.653 回答
6

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

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 将允许可变参数模板,这将使其类型安全,但现在它基本上是内存和强制转换。

你可以模拟某种可变参数->向量的东西:

// 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));
于 2009-10-16T18:55:11.100 回答
3

请参阅C、Objective-C、C++ 和 D 中的可变参数函数

您需要包含stdarg.h,然后使用va_list、和va_start,如 Wikipedia 文章中的示例所示。它比 Java 或 C# 更麻烦一些,因为 C 和 C++ 对可变参数的内置支持有限。va_argva_end

于 2009-10-16T18:47:19.530 回答
1

如果您不关心可移植性,您可以使用gcc 的语句表达式将此 C99 代码移植到 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 的 lambda 表达式做类似的事情,但是我使用的 gcc 版本(4.4.0)不支持它们。

于 2009-10-16T22:19:56.197 回答
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

按任意键继续 。. .

于 2014-12-14T16:02:22.883 回答
0

我在 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 ... 
    }
}
于 2018-04-16T02:37:25.340 回答