在C中,定义可变长度参数的唯一方法是使用省略号声明其原型并使用va_list
,va_start
,va_arg
,va_end
来提取他们。就像printf
系列和scanf
系列一样。
在C ++ 11中,引入了一种新方法,如下所示。
template <typename T, typename... MoreT>
void func(T arg, MoreT... args){
// Do some stuff
func(args);
}
每种方法有哪些好处和缺点?不鼓励他们中的任何一个在C ++中使用或鼓励吗?
答案 0 :(得分:3)
C风格的变量函数严重不鼓励。风格各不相同,但编写这些功能会让你在某些圈子(包括我的)中被扔石头,除非这是一个非常特殊的原因。
就权衡而言,C风格的可变参数函数完全类型不安全。您可以尝试从可变参数包中提取一些错误的类型,这将导致段错误。 C ++可变参数模板是强类型的,所以这是不可能的(除非你绝对强制它使用reinterpret_cast或类似的东西)。
除此之外,C ++代码通常也会(由于代码膨胀而导致极少数例外)在运行时表现更好。间接性较低,编译器可以使用的信息更多。但是,编译时间可能会更长,特别是因为可变参数模板函数(如所有模板)通常必须在头文件中定义,而C样式可变参数可以在.cpp文件中定义。
在大多数C或C ++代码(为高性能应用程序编写)中,优先级顺序通常是正确性,然后是性能,然后是编译时间。所以大多数C ++开发人员认为可变参数模板在这里是明确的,明确的赢家。
它基本上非常类似于C ++中使用模板的正确通用容器与C ++中基于void *的容器之间的比较。键入安全性+运行时性能与编译时性能(和.h与.cpp)。
答案 1 :(得分:2)
C ++努力比C更安全,例如nullptr
,enum class
提供更多类型安全的代码。编译器是你更聪明的朋友。
Variadic模板允许您更加具体地了解编译时本身的类型,而va_list
et.al中的参数。 C函数在运行时
答案 2 :(得分:1)
对于可变参数函数没有很多好处,但也有一些。
如果使用C variadic函数,则会得到一个处理所有情况的编译函数。如果使用带有可变参数模板的C ++函数,则每个参数组合可以获得一个编译函数。如果代码大小是项目的考虑因素,那么这可能是个问题。
C ++可变参数模板是类型安全的,而C变量函数则不是。这意味着编译器通常不会强制执行函数传递给可变参数函数的参数类型。 GCC和Clang支持一些解决常见情况的属性:
__attribute__((format()))
告诉编译器,可变参数使用printf
/ scanf
约定,并在参数与您的格式字符串不匹配时发出警告。已通过; __attribute__((sentinel(N)))
告诉编译器最后一个variadic参数应该是sentinel值。它仍然很可能搞砸了。例如,open
函数是可变参数,需要 mask
参数作为指定O_CREAT
时的第三个参数,但它经常被遗忘,这些__attribute__
扩展都不能解决这个问题。
标准允许但不强制要求实现允许将非POD类型传递给C变量函数;当它工作时,语义是实现定义的。这意味着如果您希望代码跨平台,则不应该这样做。
最后,学习如何正确地学习C ++可变参数模板通常比学习如何进行C变量函数更难。
答案 3 :(得分:-1)
c varadic函数是特定于实现的,并且很可能是宏,因此它们没有类型安全性。更多信息。 varadic,例如
#define va_arg(list, mode) ((mode *)(list = (char *)list + sizeof(mode)))[-1]
作为开发人员,您必须为用户定义约定,使用
执行所有这些步骤va_list, va_start, va_arg, va_end.
另一方面,c ++ 1x提供了更多的设施来解决这个问题,例如:使用类型特征,你可以检查它是否是整数,初始化列表以进行扩展等等。所有这些你可以做很多有力的工作