C ++如何在没有前面的参数的情况下使用省略号

时间:2011-03-31 03:35:06

标签: c++ variadic-functions

您好 我有一个带有成员函数的类,它接受可变数量的参数。该类知道在实例化后期望的参数数量。 e.g

class myClass
{
 myClass(int num_args){na=num_args;};
 private:
 int na;
 public:
 void do_something(int num_args, ...);
}

void myClass::do_something(int num_args, ...)
{
 va_list vl;
 va_start(vl, num_args);
 for(int i=0; i < num_args; i++)
  do_anotherthing(va_arg(vl, type));
 va_end
}

所以我最终打电话如下:

myClass A(5);
myClass B(4);

A.do_something(5, a, b, c, d, e);
B.do_something(4, a, b, c, d);

我必须继续指定我传递的参数数量似乎不整洁。什么是最好的解决方法?我考虑过重载一堆函数,每个函数都有n个参数,一个宏可以为我设置num_args。但理想情况下,我希望能够将do_something定义为

void do_something(...);

以某种方式获取stdargs的东西来处理类的num_args而不是传入的值。

非常感谢你的想法。

6 个答案:

答案 0 :(得分:2)

请不要那样做。可变数量的参数在C ++中非常罕见

实现所需目标的最佳方法是创建一个int列表并将该列表作为参数传递

你可以检查函数内的列表大小

答案 1 :(得分:1)

我不明白为什么你不能在你的功能中使用na,即

void myClass::do_something(int num_args, ...)
{
 va_list vl;
 va_start(vl, na);
 for(int i=0; i < na; i++)
   do_anotherthing(va_arg(vl, type));
 va_end
}

但是,type是什么? do_anotherthing期待什么类型?

不幸的是,可变方法不是类型安全的,如果va_arg不是您指定的类型,则vl是否会生成错误尚不清楚。考虑改为做boost::format之类的事情,即

A % a % b % c % d % e;

operator%对所提供的每个元素执行do_anotherthing,直到达到最大数量。

编辑:经过进一步考虑,最好是do_something返回一个帮助它完成所有操作的辅助对象,例如

do_something_helper myClass::do_something() { 
  return do_something_helper( na ); }

struct do_something_helper {
  int count;
  do_something_helper( int c ) : count( c ) {}

  template< class T >
  do_something_helper& operator%( T val ) {
     --count;
     if ( count < 0 ) {
      //trigger some error condition
     }
     do_anotherthing( val );
     return *this;
  }

  ~do_something_helper() {
    if ( count > 0 ) { // too few args
      //trigger some error condition
    }
  }
}

将具有使用

A.do_something() % a % b % c % d % e;

答案 2 :(得分:1)

您可以使用operator<<之类的C ++标准流,而不是使用varargs吗?然后它是所有类型安全的,你不必担心你用varargs注意到的问题。

答案 3 :(得分:0)

  

解决这个问题的最佳方法是什么?

您可以考虑使用sentinel值,这样您就不必计算元素 - 它更少(但仍然)容易出错。从使用安全的角度来看,重载可能更好,但如果参数的数量通常很大且不可预测,那么显然它变得不切实际。

更好的是,传入vector或数组。对于数组,您可以让被调用的函数知道大小ala:

template <size_t N>
void f(type (&x)[N])
{
    ...
}

您的示例表明type是常量且在编译时已知,因此vector或数组应该足够。如果这是一个简化,请考虑vector或boost variantany数组,或者可能是tuple,或者可能是一个返回对象引用的函数,你可以链接调用提供连续的值(许多运算符都这样做)。

答案 4 :(得分:0)

谢谢大家。 我不担心类是安全的类,它是一个非常小的代码,只能在严格控制的条件下使用。 我不想使用运算符(&lt;&lt;例如),因为我想要一个函数调用来获取所有参数。设置向量并传递它同样如此,我想在一次调用中完成所有操作(args总是字符串)。 我将继续指定参数的数量,或创建一个宏(叹气)。

答案 5 :(得分:0)

我知道这不是一个好习惯,并且有更好的解决方案来解决这个问题,但正如省略号参数的练习一样,我发现这是一个很好的练习。

此代码适用于我的编译器(VS2013),我很抱歉我必须使用内联汇编,但由于堆栈组织,首先想法使用本地堆栈变量无法工作。 所以这就是:

#include <iostream>
#include <cstdarg>
#include <stdint.h>

using namespace std;

double avg(double * sp, ...);

#define AVERAGE(n, ...)                         \
{                                               \
    int * stackP;                               \
    __asm {                                     \
        __asm mov ebx, esp                      \
        __asm mov stackP, ebx                   \
    }                                           \
    double a = avg(stackP, n, __VA_ARGS__);     \
    cout << "Average is: " << a << endl;        \
}

template <typename T>
double avg(int * sp, T n, ...)
{
    va_list list;
    va_start(list, n);
    double result = n;
    unsigned int count = 1;
    T * pArg = 0;

    while (sp > reinterpret_cast<int *>(pArg + 1))
    {
        pArg = &(va_arg(list, T));
        result += *pArg;
        count++;
    }

    va_end(list);

    result /= count;
    return result;
}

void main()
{
    AVERAGE(1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6);
    AVERAGE(1.2, 1.3, 1.4, 1.5, 1.6);
    AVERAGE(1.2, 1.3, 1.4);
    AVERAGE(2, 3, 4);
    AVERAGE(2, 3, 4, 5, 6);
}