4

我有班级成员:

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

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

4

5 回答 5

15

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

  • 调用者可以传递他们想要的一切。
  • 如果传递了非 POD 对象,则会调用未定义的行为
  • 您不能依赖参数的数量(调用者可能会出错)
  • 您为您的客户承担了很多责任,您希望为他们更轻松地使用您的库代码(实际示例:格式字符串错误/错误)

与可变参数模板相比:

  • 编译时间列表大小是已知的
  • 类型在编译时已知
  • 你应该对事情负责,而不是你的客户,这是应该的。

例子:

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-function 中发出编译错误。

如果您至少需要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) {}
}
于 2012-06-14T10:00:36.210 回答
3

可变参数在 C++ 中非常不受欢迎,这是有充分理由的。其中一个原因是无法知道列表的结束位置,因此如果无法更改函数的签名,则必须指定某种标记值来指示列表的结束位置。如果你不能做这两件事,你就完蛋了。

于 2012-06-14T09:48:31.017 回答
1

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

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

并且条件变为

while (a--) { ... }

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

  1. 传递 avector并使用初始化列表对其进行初始化
  2. 可变参数模板

请注意,两者之间存在差异;当转发源自{...}不是 C++ 中的表达式这一事实时,前者有一些问题。后者可能会导致代码膨胀,因为它会为每个参数类型组合生成一个函数。

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

于 2012-06-14T09:49:40.843 回答
1

您应该重写要采取的方法std::initializer_list<double>

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

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

于 2012-06-14T09:51:37.080 回答
0

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

于 2012-06-14T09:54:44.207 回答