具有可变参数的函数

时间:2010-09-02 12:55:03

标签: c++

如果我想使用函数

,我可以有哪些缺点
foo(int num, ...)

实现可变数量的参数?

我知道你只能使用一种数据类型的第一个缺点。

还有其他办法吗?

7 个答案:

答案 0 :(得分:7)

您不限于一种数据类型的参数; C(和C ++)中的printf()函数系列掩盖了这个谣言。

省略号表示法的主要缺点是你失去了类型安全性;当您使用错误类型的参数时,编译器无法告诉您。 (Go编程语言允许您指定函数采用单个类型的任意数量的参数 - 这是一个有趣的想法。)

在函数内部,必须有一些方法来告诉它提供了多少参数以及类型是什么。再次引用printf(),格式字符串告诉它预期的其他参数。现代编译器知道这些格式字符串,并且可以检查给定的参数是否与格式字符串匹配(当格式字符串是文字时)。这毕竟允许某种类型的安全 - 但是你无法使用它。使用计数是处理它的一种方法 - 但是你想知道为什么你没有使用vector<T>或类似的东西来传递数据。另一种经典方法是使用标记值 - 通常是空指针 - 在输入列表的末尾。

因此,您通常不需要可变参数列表。当你使用一个时,你通常会让自己开放出现其他机制所避免的错误。

答案 1 :(得分:4)

this question。这里最大的问题是类型安全。您将在运行时而不是编译时提取参数。 将为其实现逻辑,而不是编译器。这意味着出现错误的几率更高。不仅如此,你的代码还会被编译器真正为你完成的无关逻辑所侵扰。您不仅限于一种参数类型,但这不是一个优势。

在C ++中,有许多替代方法可以解决这个问题,其中许多方法在各方面都比省略号表示法更好。有关一些想法,请参阅that other question。经典的例子在C ++ iostreams中,与C:{/ 1>的printf()相反

std::cout << 'I' << " love h" << 3 << "r\n";

不用担心库的缺陷,它对插入运算符的使用是C ++最明亮的用途。

答案 2 :(得分:2)

有多种方法不使用省略号表示法。

为什么?由于类型安全,对基元(va_startva_argva_next)的危险操纵,你不能真正转发到另一个功能等...

然而,与C相反,C ++提供了模板方法,它提供了类型安全性和通用行为,并且可以通过重载来累积:

template <typename Arg0>
void foo(int num, Arg0 const& arg0);

template <typename Arg0, typename Arg1>
void foo(int num, Arg0 const& arg0, Arg1 const& arg1);

// ... etc

这是当前的技术水平,通常由预处理器编程的微妙应用帮助(检查Boost.Preprocessor)。

使用新的C ++ 0x标准,可变模板提供与C变量方法相同的功能,提供类型安全(yeeha)

template <typename Arg0, typename... Args>
void foo(Arg0 arg0, Args... args)
{
  // Do something with arg0
  foo(args);
}

template <typename Arg0>
void foo(Arg0 arg0)
{
  // Do something with arg0
}

这也允许更容易地定义tuple类:)

答案 3 :(得分:0)

void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    vprintf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
    return 0;
}

答案 4 :(得分:0)

一个不错的C ++替代方案就像

foo(blah, ArgList(a)(b)(c));

其中ArgList是一个重载operator ()的类,foo是一个ArgList的函数。这是将变量参数传递给函数的简明方法。根据您对不同类型的要求,您可以根据需要进行设计。

或类似

foo(blah)(a)(b)(c);

其中foo是一个重载operator ()的类。在这里创建一个临时的,析构函数将在分号后调用。

答案 5 :(得分:0)

你可以使用Loki的Functor Library,它使用类型列表作为类型安全的函数的变量参数。

答案 6 :(得分:0)

除了已经涉及的类型安全问题之外,您根本无法将非POD类型作为vararg传递。