C ++中可变参数个数?

我怎样才能写一个函数接受可变数量的参数? 这是可能的,怎么样?

你可能不应该这样做,而且你可以用更安全,更简单的方式做你想做的事情。 从技术上来说,在C中使用可变数量的参数包括stdarg.h。 从这里你将得到va_listtypes以及三个函数,它们称为va_start()va_arg()va_end()

 #include<stdarg.h> int maxof(int n_args, ...) { va_list ap; va_start(ap, n_args); int max = va_arg(ap, int); for(int i = 2; i <= n_args; i++) { int a = va_arg(ap, int); if(a > max) max = a; } va_end(ap); return max; } 

如果你问我,这是一团糟。 它看起来很糟糕,不安全,而且它的技术细节与您在概念上试图达到的目标无关。 相反,可以考虑使用重载或inheritance/多态,构造器模式(如在stream中的operator<<()中)或默认参数等。这些都更安全:编译器更了解你想要做什么,更多的情况下,它可以阻止你之前,你吹了你的腿。

C ++ 11中 ,有两个新的选项,如Alternatives部分中的Variadic函数参考页所示:

  • 也可以使用variables模板来创build可变数量参数的函数。 它们往往是更好的select,因为它们不会对参数的types施加限制,也不会执行整数和浮点升级,并且是types安全的。 (自C ++ 11以来)
  • 如果所有可变参数共享一个公共types,std :: initializer_list提供了一个访问variables参数的方便机制(虽然语法不同)。

下面是一个显示两个替代scheme的例子( 看它现场 ):

 #include <iostream> #include <string> #include <initializer_list> template <typename T> void func(T t) { std::cout << t << std::endl ; } template<typename T, typename... Args> void func(T t, Args... args) // recursive variadic function { std::cout << t <<std::endl ; func(args...) ; } template <class T> void func2( std::initializer_list<T> list ) { for( auto elem : list ) { std::cout << elem << std::endl ; } } int main() { std::string str1( "Hello" ), str2( "world" ); func(1,2.5,'a',str1); func2( {10, 20, 30, 40 }) ; func2( {str1, str2 } ) ; } 

如果您使用的是gccclang我们可以使用PRETTY_FUNCTION 魔术variables来显示函数的types签名,这有助于理解正在发生的事情。 例如使用:

 std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ; 

这个例子中的可变参数函数的结果如下( 见它的实况 ):

 void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1 void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5 void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a void func(T) [T = std::basic_string<char>]: Hello 

在Visual Studio中,你可以使用FUNCSIG

更新Pre C ++ 11

Pre C ++ 11 std :: initializer_list的替代方法是std :: vector或其他标准容器之一 :

 #include <iostream> #include <string> #include <vector> template <class T> void func1( std::vector<T> vec ) { for( typename std::vector<T>::iterator iter = vec.begin(); iter != vec.end(); ++iter ) { std::cout << *iter << std::endl ; } } int main() { int arr1[] = {10, 20, 30, 40} ; std::string arr2[] = { "hello", "world" } ; std::vector<int> v1( arr1, arr1+4 ) ; std::vector<std::string> v2( arr2, arr2+2 ) ; func1( v1 ) ; func1( v2 ) ; } 

可变参数模板的替代scheme将是可变参数函数,虽然它们不是types安全的 ,一般来说容易出错,并且可能是不安全的,但唯一可能的替代scheme是使用默认参数 ,尽pipe这样做的用处有限。 下面的例子是链接引用中示例代码的修改版本:

 #include <iostream> #include <string> #include <cstdarg> void simple_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); while (*fmt != '\0') { if (*fmt == 'd') { int i = va_arg(args, int); std::cout << i << '\n'; } else if (*fmt == 's') { char * s = va_arg(args, char*); std::cout << s << '\n'; } ++fmt; } va_end(args); } int main() { std::string str1( "Hello" ), str2( "world" ); simple_printf("dddd", 10, 20, 30, 40 ); simple_printf("ss", str1.c_str(), str2.c_str() ); return 0 ; } 

使用可变参数函数也可以传递参数的限制,在第5.2.2函数调用7段的C ++标准草案中对此进行了详细说明:

当给定参数没有参数时,parameter passing的方式是接收函数可以通过调用va_arg(18.7)来获得参数的值。 在参数expression式上执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)的标准转换。 在这些转换之后,如果参数不具有算术,枚举,指针,成员指针或类types,则该程序是格式不正确的。 如果参数具有非POD类types(第9节),则行为是未定义的。 […]

在C ++中支持C风格的可变参数函数。

然而,大多数C ++库使用了另一种习惯用法,例如, 'c' printf函数采用可变参数, c++ cout对象使用<<重载哪些地址types安全和ADT(可能以实现简单为代价)。

在C ++ 11中,有一种方法可以实现可变的参数模板,这样就可以实现一个非常优雅和安全的方法来获得可变的参数函数。 Bjarne自己给出了一个在C ++ 11FAQ中 使用可变参数模板的printf的好例子。

就我个人而言,我认为这是如此的优雅,我甚至不会在C ++中使用可变参数函数,直到该编译器支持C ++ 11variables参数模板。

除了可变参数或重载,你可以考虑在std :: vector或其他容器(例如std :: map)中聚合你的参数。 像这样的东西:

 template <typename T> void f(std::vector<T> const&); std::vector<int> my_args; my_args.push_back(1); my_args.push_back(2); f(my_args); 

通过这种方式,你将获得types安全性,这些可变参数的逻辑意义将是显而易见的。

当然这种方法可能会有性能问题,但除非您确定无法支付价格,否则您不应该担心。 这是c ++的一种“Pythonic”方法…

唯一的方法是通过使用C风格的variables参数,如下所述。 请注意,这不是一个推荐的做法,因为它不是types安全且容易出错的。

没有标准的C ++方法来做到这一点,而不诉诸C型可变参数( ... )。

当然有默认参数,根据上下文的不同,可以根据不同的参数types来“查找”:

 void myfunc( int i = 0, int j = 1, int k = 2 ); // other code... myfunc(); myfunc( 2 ); myfunc( 2, 1 ); myfunc( 2, 1, 0 ); 

所有四个函数调用都以不同数量的参数调用myfunc 。 如果没有给出,则使用默认参数。 但请注意,您只能省略尾随参数。 没有办法,例如省略i ,只给j

在C ++ 11中,你可以这样做:

 void foo(const std::list<std::string> & myArguments) { //do whatever you want, with all the convenience of lists } foo({"arg1","arg2"}); 

列表初始化程序FTW!

可能需要重载或默认参数 – 使用默认参数定义相同的函数:

 void doStuff( int a, double termstator = 1.0, bool useFlag = true ) { // stuff } void doStuff( double std_termstator ) { // assume the user always wants '1' for the a param return doStuff( 1, std_termstator ); } 

这将允许您使用四种不同的调用之一调用该方法:

 doStuff( 1 ); doStuff( 2, 2.5 ); doStuff( 1, 1.0, false ); doStuff( 6.72 ); 

…或者你可能正在寻找从C的v_args调用约定

正如其他人所说,C型可变参数。 但是你也可以用默认参数做类似的事情。

如果你知道提供的参数个数的范围,你总是可以使用一些函数重载

 f(int a) {int res=a; return res;} f(int a, int b) {int res=a+b; return res;} 

等等…

 int fun(int n_args, ...) { int *p = &n_args; int s = sizeof(int); p += s + s - 1; for(int i = 0; i < n_args; i++) { printf("A1 %d!\n", *p); p += 2; } } 

普通版本

如果所有参数都是常量,并且types相同,我们也可以使用initializer_list