函数中的参数数量未知

时间:2012-06-14 09:44:58

标签: c++ argument-passing variadic-functions

我有班级成员:

LineND::LineND(double a ...)
{
    coefficients.push_back(a);
    va_list arguments;
    va_start(arguments, a);
    double argValue;
    do
    {
        argValue = va_arg(arguments, double);
        coefficients.push_back(argValue);
    }while(argValue != NULL);   // THIS IS A PROBLEM POINT!
    va_end(arguments);
}

我不知道将使用多少个参数。我需要将每个参数都放入名为coefficients的向量中。我该怎么办?我理解,在这种情况下,while(argValue != NULL)语句不正确。我不能使用例如这个签名:

LineND::LineND(int numArgs, double a ...)

改变这样的条件:

while(argValue != numArgs);

关键是我无法改变方法的签名。需要以另一种方式解决这个问题。

5 个答案:

答案 0 :(得分:15)

变量参数列表有几个缺点:

  • 来电者可以传递他们想要的一切。
  • 如果传递了非POD对象,则会召唤未定义的行为
  • 您不能依赖参数的数量(来电者可以犯错误)
  • 您对您的CLIENT负有很多责任,对您而言,您希望更轻松地使用库代码(实际示例:format-string-bugs / -errors)

与可变参数模板相比:

  • 编译时间列表大小已知
  • 类型在编译时已知
  • 你有责任,而不是你的客户,就像它应该的那样。

示例:

void pass_me_floats_impl (std::initializer_list<float> floats) {
    ...
}

您可以将其放入类声明的私有部分或某些详细命名空间中。注意:pass_me_floats_impl()不必在标题中实现。

然后这是给你的客户好的东西:

template <typename ...ArgsT>
void pass_me_floats (ArgsT ...floats) {
    pass_me_floats_impl ({floats...});
}

他现在可以做到:

pass_me_floats ();
pass_me_floats (3.5f);
pass_me_floats (1f, 2f, 4f);

但他做不到:

pass_me_floats (4UL, std::string());

因为这会在你的pass_me_floats函数中发出编译错误。

如果你需要至少,比方说2个参数,那么就这样做:

template <typename ...ArgsT>
void pass_me_floats (float one, float two, ArgsT... rest) {}

当然,如果你想要一个完整的内联函数,你也可以

template <typename ...ArgsT>
void pass_me_floats (ArgsT... rest) {
    std::array<float, sizeof...(ArgsT)> const floaties {rest...};

    for (const auto f : floaties) {}
}

答案 1 :(得分:3)

可变参数在C ++中非常不受欢迎,并且有充分的理由。其中一个原因是无法知道列表的结束位置,因此如果您无法更改函数的签名,则必须指示某种类型的标记值以指示列表的位置结束。如果你不能做其中任何一件事,那你就搞砸了。

答案 2 :(得分:1)

在这种情况下,您无法确定代码中的参数数量。你必须让这个函数的调用者传递给你double的数量(我假设第一个a参数不包含参数的数量)。例如:

LineND::LineND(unsigned count, ...)
{
    va_list arguments;
    va_start(arguments, a);

,条件变为

while (a--) { ... }

在C ++ 11中,您可以使用两个更好的设备来传递未指定数量的双精度数。

  1. passing a vector and initializing it with an initializer-list
  2. variadic templates
  3. 请注意,两者之间存在差异;因为{...}不是C ++中的表达式而导致转发时,前者有一些问题。后者会导致代码膨胀,因为它为每个参数类型组合生成一个函数。

    在C ++ 03中,您只能使用辅助对象(例如Boost.Assignment库提供的辅助对象)来传递未知数量的参数。

答案 3 :(得分:1)

您应该重写方法以取std::initializer_list<double>

如果您不能这样做,并且无法添加count参数,则修复方法是采用终止参数列表的 sentinel 值,例如0.0NaN(或任何在您的函数上下文中没有意义的值)。

例如,采用可变数量指针的函数使用NULL作为sentinel值;采用结构列表的函数将0初始化结构作为标记。这在C API中相当常见;请参阅http://c-faq.com/varargs/nargs.html,其中给出的示例为execl("/bin/sh", "sh", "-c", "date", (char *)NULL);

答案 4 :(得分:0)

使参数列表的第一个双精度为实际的参数计数。