3.16 含有可变参数的函数
用于编写函数参数不确定的函数,可以定义可变长度的形参表
3.16.1 含有可变参数的函数
C++标准中提供了两种主要的方法
-
如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型
initializer_list
initializer_list是一种标准库类型,用于表示某种特定类型的值的数组,该类型定义在同名的头文件中,也就是要include这个头文件(使用时需
#include <initializer_list>) -
如果实参的类型不同,我们可以编写可变参数的
模板
3.16.2 initializer_list提供的操作
| initializer_list提供的操作 | 说明 |
|---|---|
initializer_list<T> lst; |
默认初始化;T类型元素的空列表 |
initializer_list<T> lst{a, b, c...} |
lst的元素数量和初始值一样多;lst的元素是对应初始值的副本;列表中的元素是const |
lst2(lst) lst2 = lst |
拷贝或者赋值一个initializer_list对象但不拷贝列表中的元素;拷贝后原始列表和副本共享元素 |
lst.size() |
列表中的元素数量 |
lst.begin() |
返回指向lst首元素的指针 |
lst.end() |
返回指向lst尾元素下一位置的指针 |
3.16.3 initializer_list的使用方法
initializer_list是一个类模板
使用模板时,我们需要在模板名字后面跟一对尖括号,括号内给出类型参数
C++
initializer_list<string> ls;//initializer_list的元素类型是string
initializer_list<int> li;//initializer_list的元素类型是int
initializer_list比较特殊的一点是,其对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值
简单来说:你可以读取initializer_list里的元素,但永远不能修改这些元素的值------哪怕你想在代码里直接赋值,编译器也会报错。下面用"通俗解释+代码示例"帮你彻底理解:
一、通俗理解
把initializer_list想象成一个"只读的购物清单":
- 你可以看清单上的每一项(读取元素);
- 但你不能用笔把清单上的"苹果"改成"香蕉"(修改元素);
- 若想改,只能重新写一份新清单(创建新的
initializer_list),原清单永远不变。
二、代码示例:验证"无法修改元素"
下面的代码能直观体现这个特性,你可以复制到编译器中运行,看报错结果:
cpp
#include <iostream>
#include <initializer_list>
using namespace std;
int main() {
// 1. 创建一个initializer_list<int>,包含元素1、2、3
initializer_list<int> lst = {1, 2, 3};
// 2. 尝试修改第一个元素(编译器直接报错)
// 错误写法:试图给常量元素赋值
// lst.begin()[0] = 10; // 这行代码会编译失败!
// 3. 正确操作:只能读取元素,不能修改
cout << "读取第一个元素:" << lst.begin()[0] << endl; // 输出1
for (auto num : lst) {
cout << num << " "; // 输出1 2 3(正常读取)
}
// 4. 若想"更新"元素,只能创建新的initializer_list
initializer_list<int> new_lst = {10, 2, 3}; // 新清单,和原清单无关
cout << "\n新列表第一个元素:" << new_lst.begin()[0] << endl; // 输出10
return 0;
}
关键报错说明
如果你取消注释lst.begin()[0] = 10;这行,编译器会抛出类似错误:
error: assignment of read-only location '* lst.std::initializer_list<int>::begin()'
翻译过来就是:"试图给只读的内存位置赋值"------本质是initializer_list的元素被编译器标记为const(常量),禁止写入操作。
三、为什么initializer_list要设计成"元素只读"?
这是C++标准的刻意设计,核心原因有2个:
- 简化底层实现 :
initializer_list的拷贝/赋值不会复制元素(只是共享底层数组),如果允许修改元素,一个列表的修改会影响所有拷贝版本,引发不可控的bug; - 语义匹配 :
initializer_list的核心用途是"传递一组初始值"(比如给函数传可变参数),而非"存储可修改的数据集"------若需要可修改的集合,应该用vector、array等容器。
四、和普通容器(如vector)的对比
用表格能更清晰看出差异:
| 操作 | initializer_list | vector(普通容器) |
|---|---|---|
| 读取元素 | ✅ 支持 | ✅ 支持 |
修改元素(如v[0]=10) |
❌ 禁止(编译报错) | ✅ 支持 |
| 元素底层属性 | const(常量) | 普通变量(可读写) |
| 核心用途 | 传递可变初始值 | 存储/修改数据集 |
总结
initializer_list的元素是只读常量:只能读、不能改,修改会触发编译错误;- 若需修改元素,不要用
initializer_list,改用vector、array等可读写容器; - 这个设计是为了保证
initializer_list的轻量性(拷贝共享元素)和语义安全性(避免意外修改)。
含有initializer_list形参的函数也可以同时拥有其他形参
3.16.4 initializer_list使用
在编写代码输出程序产生的错误信息时,最好统一用一个函数实现该功能,使得对所有错误的处理能够整齐划一,然而错误信息的种类不同,调用错误信息输出函数时传递的参数也会各不相同
使用initializer_list编写一个错误信息输出函数使其可以作用于可变参数的形参