如果我想使用函数
,我可以有哪些缺点foo(int num, ...)
实现可变数量的参数?
我知道你只能使用一种数据类型的第一个缺点。
还有其他办法吗?
答案 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_start
,va_arg
,va_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传递。