我怎么能写一个函数,接受可变数量的参数?这可能吗?怎么可能?
当前回答
c++支持C风格的变进函数。
然而,大多数c++库使用另一种习惯,例如,' C ' printf函数接受变量参数,而c++ cout对象使用<<重载来解决类型安全和adt(可能以实现简单性为代价)。
其他回答
你可能不应该这样做,你可以用一种更安全、更简单的方式做你想做的事情。从技术上讲,要在C语言中使用可变数量的参数,需要包含stdarg.h。由此,您将获得va_list类型以及对其进行操作的三个函数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;
}
要我说,这真是一团糟。它看起来很糟糕,不安全,而且充满了与你在概念上试图实现的目标无关的技术细节。相反,可以考虑使用重载或继承/多态性、构建器模式(如流中的操作符<<())或默认参数等。这些都是更安全的:编译器会更多地了解你要做什么,这样就有更多的机会在你断腿之前阻止你。
正如其他人所说,c风格的变量。但是你也可以对默认参数做类似的事情。
如果不使用C风格的可变参数(…),就没有标准的c++方法可以做到这一点。
当然,根据上下文,有一些默认参数“看起来”像可变数量的参数:
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中,你有两个新的选项,正如可变参数参考页中alternative部分所述:
可变参数模板也可用于创建具有可变数量的参数的函数 参数。它们通常是更好的选择,因为它们不施加限制 参数的类型,不执行整数和浮点提升,以及 类型安全。(因为c++ 11) 如果所有变量参数共享一个公共类型,std::initializer_list将提供一个 访问变量参数的便利机制(尽管语法不同)。
下面是一个展示这两种选择的例子(看现场):
#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 } ) ;
}
如果您正在使用gcc或clang,我们可以使用PRETTY_FUNCTION魔术变量来显示函数的类型签名,这有助于理解正在发生的事情。例如:
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
在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 ) ;
}
可变变量模板的替代方法是可变变量函数,尽管它们不是类型安全的,而且通常容易出错,使用起来不安全,但唯一的其他潜在替代方法是使用默认参数,尽管它的用途有限。下面的例子是链接参考中样例代码的修改版本:
#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 ;
}
使用可变值函数也会在参数中带来限制,这在c++标准草案5.2.2节函数调用第7段中有详细说明:
When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7). The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. [...]
c++支持C风格的变进函数。
然而,大多数c++库使用另一种习惯,例如,' C ' printf函数接受变量参数,而c++ cout对象使用<<重载来解决类型安全和adt(可能以实现简单性为代价)。
推荐文章
- 为什么这个结合赋值和相等检查的if语句返回true?
- cplusplus.com给出的错误、误解或坏建议是什么?
- 找出质数最快的算法是什么?
- c++枚举类可以有方法吗?
- 格式化IO函数(*printf / *scanf)中的转换说明符%i和%d之间的区别是什么?
- 将析构函数设为私有有什么用?
- main()中的Return语句vs exit()
- 为什么c#不提供c++风格的'friend'关键字?
- 在函数的签名中添加关键字
- 我如何在Visual Studio中预处理后看到C/ c++源文件?
- 为什么在标准容器中使用std::auto_ptr<>是错误的?
- 用比较double和0
- 保护可执行文件不受逆向工程的影响?
- 在c++中字符串前面的“L”是什么意思?
- 为什么std::map被实现为红黑树?