C语言可变参数
接口概览
cpp
#include <stdarg.h>
va_list ap; // 参数指针类型
va_start(ap, last); // 初始化:last必须是最后一个固定参数
va_arg(ap, type); // 获取下一个参数,type指定类型
va_end(ap); // 清理
va_copy(dest, src); // 复制参数指针(C99),一般来说va_arg调用一次指针就会后移指向下一个参数, /这个函数可以记录下之前某个参数的位置方便回退
使用示例
cpp
#include<stdarg.h>
//使用参数来标记有多少个可变参数
string func(int count,...)
{
va_list list;
va_list list2;
va_start(list,count);
va_start(list2, count);
int sum = 0;
for (int i = 0;i<count;i++)
{
if (i == 1)
{
va_copy(list2,list);
}
sum += va_arg(list,int);
}
int sum2 = 0;
for (int i = 0;i<count-1; i++)
{
sum2 += va_arg(list2, int);
}
va_end(list);
va_end(list2);
return string(to_string(sum)+":"+ to_string(sum2));
}
//使用尾标记来标记有多少个可变参数
int func2(int a,...)
{
va_list list;
va_start(list,a);
int sum = 0;
while(1)
{
char* temp = va_arg(list,char*);
if (temp == nullptr)break;
sum += stoi(temp);
}
va_end(list);
return sum;
}
int main()
{
string sum = func(4,1,2,3,4);
int sum2 = func2(0,"1","2","3",nullptr);
cout << sum << endl;
cout << sum2 << endl;
}
原理概览
了解过函数栈帧的同学应该都知道,函数调用之后会把其收到的参数存放在函数栈中(一般是连续的),因此这个C语言可变参数出乎意料的简单:

特别注意
使用C语言可变参数的时候,要特别注意,va_start的调用之前最好不要调用其他函数,因为函数调用可能会导致可变参数在栈上的位置被优化等等,使得va_start无法找到最后一个固定参数的位置
C++可变参数
initializer_list(C++11)
cpp
void func(initializer_list<int> list)
{
for(auto it = list.begin();it!=list.end();++it)
{
cout << (*it) << " ";
}
cout << endl;
}
int main()
{
func({1, 5, 6, 3, 9});
}
原理:其实就是编译器把{1,5,6,3,9}转换成一个只读数组,然后把这个数组的指针等等信息存放到initializer_list对象中。
可变参数模版
递归展开参数包:
cpp
void get_args(vector<int>& arr)
{
return;
}
template<typename T,typename... Args>
void get_args(vector<int>& arr,T t,Args... args)
{
arr.push_back(t);
get_args(arr,args...);
}
template<typename... Args>
void func(Args... args)//定义了一个可变参数包
{
vector<int> arr;
get_args(arr,args...);//展开可变参数
cout << sizeof...(args) << endl;//sizeof...是专门获取可变参数包参数个数的操作符
for (const auto& e : arr)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
func(5, 6, 3, 2, 7);
}
直接展开参数包:
cpp
template<typename... Args>
vector<int> fun(Args... args)
{
int arr[] = {args...};
for (const auto& e : arr) { cout << e << " "; }
cout << endl;
return {args...};
}
int main()
{
vector<int>ret = fun(5, 6, 3, 2, 7);
for (const auto& e : ret) { cout << e << " "; }
cout << endl;
}
折叠表达式:
cpp
1. 一元左折叠 (... op pack)
template<typename... Args>
auto sum(Args... args) {
return (... + args); // ((args1 + args2) + args3) + ...
}
2. 一元右折叠 (pack op ...)
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // args1 + (args2 + (args3 + ...))
}
3. 二元左折叠 (init op ... op pack)
template<typename... Args>
auto sum_with_init(Args... args) {
return (0 + ... + args); // (((0 + args1) + args2) + args3)
}
4. 二元右折叠 (pack op ... op init)
template<typename... Args>
auto sum_with_init(Args... args) {
return (args + ... + 0); // args1 + (args2 + (args3 + 0))
}
原理
C++可变参数模版的的原理很简单:递归展开方式的原理就是递归模版实例化,而折叠表达式的原理就是编译时期直接展开。