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

And*_*hin 31 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);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*202 44

这些被称为Variadic函数.维基百科列出了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;
}
Run Code Online (Sandbox Code Playgroud)

  • @Licky:如上所述,我复制了维基百科的代码.我强调说,对于C++,应该使用`cstdarg`标头.对我来说似乎很清楚. (2认同)

rub*_*nvb 15

真正的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
Run Code Online (Sandbox Code Playgroud)

它为每个参数集合生成一个符号,然后为递归的每个步骤生成一个符号.至少可以说这不是最理想的,所以SO的优秀C++人员想到了滥用列表初始化的副作用一个很好的技巧:

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

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


Seb*_*Seb 7

在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 } );
Run Code Online (Sandbox Code Playgroud)


GMa*_*ckG 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);
Run Code Online (Sandbox Code Playgroud)

但是,您无法在参数,表单中调用它.您必须使用答案中列出的任何可变参数函数.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
Run Code Online (Sandbox Code Playgroud)

用法:

func(gen_vec(1,2,3));
Run Code Online (Sandbox Code Playgroud)