c++ 可变参数
方法一: C语言的: va_list1
#include <stdio.h>
#include <stdarg.h>
int add_nums(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count); // C23 起能省略 count
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
int main(void)
{
printf("%d\n", add_nums(4, 25, 25, 50, 50));
}
c++ 可变参数
方法一: C语言的: va_list1
#include <stdio.h>
#include <stdarg.h>
int add_nums(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count); // C23 起能省略 count
for (int i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
int main(void)
{
printf("%d\n", add_nums(4, 25, 25, 50, 50));
}
\1. 函数本身并不知道传进来几个参数,比如我现在多传一个参数,或者少传一个参数,那么函数本身是检测不到这个问题的。这就可能会导致未定义的错误。
\2. 函数本身也不知道传进来的参数类型。以上面的例子,假如我把第二个参数1.0改成一个字符串,又如何?答案就是会得到未定义的错误,也就是不知道会发生什么。
\3. 对于可变长参数,我们只能用__cdecl调用约定,因为只有调用者才知道传进来几个参数,那么也只有调用者才能维持栈平衡。如果是__stdcall,那么函数需要负责栈平衡,可是函数本身根本不知道有几个参数,函数调用结束后,根本不知道需要将几个参数pop out。(注:某些编译器如VS,如果用户写了个__stdcall的可变长参数函数,VS会自动转换成__cdecl的,当然这是编译器干的事情)
方法二:C++语言的: initializer_list
initializer_list API
void my_print1(int a, initializer_list<string> il)
{
cout << a << endl;
for (auto &i : il)
cout << i << " ";
cout << endl;
}
//函数调用
int main()
{
my_print1(2, {"lxb", "zhj"});
return 0;
}
方法三:c++ 使用可变参数模板
. 函数本身并不知道传进来几个参数,比如我现在多传一个参数,或者少传一个参数,那么函数本身是检测不到这个问题的。这就可能会导致未定义的错误。
. 函数本身也不知道传进来的参数类型。以上面的例子,假如我把第二个参数1.0改成一个字符串,又如何?答案就是会得到未定义的错误,也就是不知道会发生什么。
. 对于可变长参数,我们只能用__cdecl调用约定,因为只有调用者才知道传进来几个参数,那么也只有调用者才能维持栈平衡。如果是__stdcall,那么函数需要负责栈平衡,可是函数本身根本不知道有几个参数,函数调用结束后,根本不知道需要将几个参数pop out。(注:某些编译器如VS,如果用户写了个__stdcall的可变长参数函数,VS会自动转换成__cdecl的,当然这是编译器干的事情)
方法二:C++语言的: initializer_list
initializer_list API
void my_print1(int a, initializer_list<string> il)
{
cout << a << endl;
for (auto &i : il)
cout << i << " ";
cout << endl;
}
//函数调用
int main()
{
my_print1(2, {"lxb", "zhj"});
return 0;
}
方法三:c++ 使用可变参数模板
c++ 可变参数
方法一: C语言的: va_list1
#include <stdio.h一个可变参数模板(variadic template)就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包(parameter packet)。存在两种参数包:模板参数包(template parameter packet),表示零个或多个模板参数;函数参数包function parameterpacket),表示零个或多个函数参数。
C++11新特性: 可变参数模版
3.1 可变参数个数 可变参数的个数: template<class... Types> struct count { static const std::size_t value = sizeof...(Types); }; 3.2 递归展开可变参数 通过递归的方式展开递归展开需要考虑爆栈的情况。 说到这里,ubuntu(linux) 默认栈大小8M(使用命令 ulimit -a查看), Win下,Visual Studio 默认栈大小1M(连接器->系统)。
需要两个函数:递归终止函数 和 递归函数
一个例子,输出各个元素。函数参数为参数包的形式,使用递归展开
double Sum2() // 边界条件 { return 0; } template<typename T1, typename... T2> double Sum2(T1 p, T2... arg) { double ret = p + Sum2(arg...); return ret; } int main() { cout << Sum2(2,2,2,2); } 递归终止改为一个参数 template<class T> void print(T &t) {cout << t <<'\n';} template<class T, class... Args> void print(T &t, Args&... rest) { cout << t << ' '; print(rest...); // 打印剩余参数,注意省略号必须有 } 3.3 逗号表达式展开使用逗号运算符是为了把几个表达式放在一起。
整个逗号表达式的值为系列中最后一个表达式的值。
从本质上讲,逗号的作用是将一系列运算按顺序执行。

当然,这里其实不用逗号表达式也可以,直接给PrintArg函数带上返回值即可完成逗号表达式的功能:
template <class T> int PrintArg(const T& t) { cout << t << " "; return 0; } template <class ...Args> void ShowList(Args... args) { //列表初始化 int arr[] = { PrintArg(args)... }; cout << endl; }此时可以传入多种类型的参数了,但是不能不传参数,因为数组的大小不能为0,为了支持不传参数,我们需要单独写个无参的ShowList函数,就像无参版的终止函数那样:
//支持无参调用 void ShowList() { cout << endl; } template <class T> int PrintArg(const T& t) { cout << t << " "; return 0; } template <class ...Args> void ShowList(Args... args) { //列表初始化 int arr[] = { PrintArg(args)... }; cout << endl; }这个数组的目的纯粹是为了在数组构造的过程展开参数包。