Part 1 - initializer_list
only C++
适用:参数类型相同,但参数个数不定
来历:C++ 标准库
使用它,需要包含头文件initializer_list,命名空间为std
俯瞰#include端详int add(initializer_list nums) { int result = 0; for (auto nitr = nums.begin(); nitr != nums.end(); nitr++) { result += (*nitr); } return result; } // 调用 int main() { // 注意:不要漏掉花括号 int r = add({-3, 2, 5, 1}); return 0; }
#includeusing namespace std; initializer_list list; // 默认初始化,空列表 initializer_list list{e1, e2, e3, ...}; // 另一种初始化方式 list2 = list1; // 拷贝 list.begin(); // 列表首元素指针 list.end(); // 列表最后一个元素下一个位置的指针 list.size(); // 列表中的元素数量
initializer_list是一个模板类型
initializer_list是一个线性容器,我们姑且称之为“列表”
initializer_list需要接受一个类模板参数
e.g. initializer_list
取用initializer_list里面的元素的方式,类似于vector等STL容器
对于一个initializer_list对象:
调用begin()与end(),分别来获取列表的头和列表最后一个元素的下一个位置的指针,需要注意的是,和vector等STL容器不同,begin()或end()返回的是const T *型的裸指针调用size()来获取列表长度initializer_list对象之间的拷贝或赋值,只改变列表对象本身,而在内存中共享同一份存储的数据initializer_list对象中存储的数据都是拷贝过来的副本,且都为const型,无法更改
// 头文件、命名空间等代码已省略
int main()
{
initializer_list lst;
lst = {1, 3, 2, 4};
const int * np = lst.begin(); // 返回的是原始对象的const型裸指针
*np = 3; // 错误: 列表里面的值都为const型,不能修改
initializer_list lst2 = lst;
cout<< (lst2.begin() == lst.begin()) << endl; // 1,即true:赋值或拷贝时在内存中共享元素
return 0;
}
那么,试试把initializer_list与函数形参结合起来:
// 头文件、命名空间等代码已省略 int add(initializer_listnums) { int result = 0; // 这里的 auto 也可以写成 const int * for (auto nitr = nums.begin(); nitr != nums.end(); nitr++) { result += (*nitr); } return result; } int main() { cout << add({1, 2, 5}) << endl; // 输出:8 return 0; }
Part 2 - stdarg/varargs 省略符形参
Both C and C++
适用:不定参数类型,不定参数个数
来历:C标准库
使用它,需要包含头文件stdarg.h
俯瞰#include端详#include using std::cout; using std::endl; // n: 几个数字相加 // 调用时,我们在最后再传一个字符串进去,让它把字符串打印出来 int add(int n, ...) { va_list lst; va_start(lst, n); int result = 0; for (int i = 0; i < n; i++) { result += va_arg(lst, int); } cout << va_arg(lst, const char *) << endl; va_end(lst); return result; } int main() { int r = add(3, 1, 8, 2, "Hello World"); // r = 11 cout << "The result is " << r << endl; return 0; }
#includetypedef char * va_list; // va_list 的底层定义 void va_start(va_list ap, last); // 宏,初始化ap type va_arg(va_list ap, type); // 宏,取参数 void va_copy(va_list dest, va_list src); // 宏,拷贝 va_list 对象 void va_end(va_list ap); // 宏,结束时调用
va_...()都是宏va_list是一个类型,实质上是一个char *,表示所操作的函数实参列表,此类对象的作用类似于句柄,因此,每个va_...()宏的第一个参数都是va_list型对象一般的操作流程为:
- 定义va_list型对象,假设其为lst.调用va_start()宏,第二个参数填函数的可变参数开始之前的一个形参名,这是为了定位到第一个可变参数开始的地址,但是,在现代编译器中,此参数可以填别的什么已定义的标识符,编译器会警告,但可以正常发挥作用调用va_arg()宏,第二个参数是一个类型名,表示你当前取的参数的类型,va_arg()会按顺序取参数,第一次调用时就取第一个参数,第二次调用时就取第二个参数,以此类推调用va_end()宏,结束对可变参数列表的使用
一份示例代码
#include#include #include #include using std::cout; using std::endl; using std::string; using std::initializer_list; // Part 1 - initializer_list void say_case1(initializer_list strs) { for (const string * itr = strs.begin(); itr != strs.end(); itr++) { cout << *itr << endl; } } // Part 2 - stdarg/varargs 省略符形参 // Param n: 实参数量 // 最后传进去两个数字,然后输出相加的结果 void sayAndAdd_case2(int n, ...) { va_list lst; va_start(lst, n); for (int i = 0; i < n - 2; i++) { cout << va_arg(lst, const char *) << endl; } int a = va_arg(lst, int); int b = va_arg(lst, int); cout << a << " + " << b << " = " << a + b << endl; va_end(lst); } int main() { say_case1({"Hello", "I am initializer_list."}); sayAndAdd_case2(5, "Hi", "I am varargs.", "Nice to Meet youuu~", 3, 5); return 0; }
附:
std::initializer_list
stdarg(3) — Linux manual page



