直接列表初始化中的 auto 推导规则
先回顾 auto
auto 让编译器根据初始化表达式推导变量类型。
auto a = 10; // int
auto b = 3.14; // double
auto c = 'A'; // char
普通情况下,可以简单理解为:右边是什么类型,左边的 auto 就推导成相应类型。但花括号初始化比较特殊。
两种列表初始化
使用花括号初始化时,常见形式有两种。
复制列表初始化
auto x = {1, 2, 3};
特点是 auto 后面有等号。
直接列表初始化
auto x{1};
特点是 auto 后面直接跟花括号,没有等号。
过去:C++11 和 C++14
在早期规则中,auto 遇到花括号时通常会优先推导为 std::initializer_list。
auto a = {1, 2, 3}; // initializer_list<int>
auto b{1}; // initializer_list<int>
这会让下面的代码显得不太符合直觉:
auto x{10};
很多人会认为 x 应该是一个整数,但旧规则中它是只包含一个整数的初始化列表。
现在:C++17 的规则
C++17 修改了直接列表初始化的推导方式。
单元素直接列表初始化
auto x{10};
从 C++17 开始,x 推导为 int。
auto a{3.14}; // double
auto b{'A'}; // char
auto c{true}; // bool
多元素直接列表初始化
auto x{1, 2}; // 编译错误
C++17 中,直接列表初始化要求花括号中只能有一个元素。
如果确实需要一个初始化列表,应写成:
auto x = {1, 2}; // initializer_list<int>
带等号的规则没有改变
C++17 只改变了直接列表初始化。复制列表初始化仍然推导为 initializer_list。
auto a = {1}; // initializer_list<int>
auto b = {1, 2, 3}; // initializer_list<int>
因此最需要区分的是:
元素类型必须一致
复制列表初始化要求花括号中的元素能够推导出同一种类型。
auto a = {1, 2, 3}; // 正确
auto b = {1, 2.5}; // 错误
第二行同时出现 int 和 double,编译器无法确定统一的列表元素类型。
可以显式指定类型:
std::initializer_list<double> b{1, 2.5};
规则对照
| 代码 | C++11/14 | C++17 |
auto x = {1}; | initializer_list<int> | 相同 |
auto x = {1,2}; | initializer_list<int> | 相同 |
auto x{1}; | initializer_list<int> | int |
auto x{1,2}; | 旧规则较特殊 | 编译错误 |
教学中重点记住 C++17 这一列即可。
为什么要修改
C++17 的调整是为了让代码更符合普通变量初始化的直觉:
auto x{10};
现在它更接近:
int x{10};
因此 C++17 可以简单概括为:
- 直接列表初始化只有一个元素时,按该元素类型推导;
- 直接列表初始化有多个元素时,编译失败。
常见例子
整数与浮点数
auto a{10}; // int
auto b = {10}; // initializer_list<int>
auto c{3.14}; // double
auto d = {3.14}; // initializer_list<double>
字符串字面量
auto s{"hello"};
这里推导出的不是 std::string,而是字符串字面量对应的指针类型。
若需要字符串对象,应显式写:
std::string s{"hello"};
常见错误
误把单元素直接初始化当作列表
auto x{1};
x.size(); // 错误,x 是 int
直接列表中放多个元素
auto x{1, 2, 3}; // 错误
需要初始化列表时应写:
auto x = {1, 2, 3};
混合不同类型
auto x = {1, 2.0}; // 错误
忘记启用 C++17
g++ main.cpp -std=c++17 -o main
课堂练习
判断下列代码在 C++17 中的推导结果,或说明是否编译错误。
auto a{1};
auto b = {1};
auto c{1.5};
auto d = {1.5};
auto e{1, 2};
auto f = {1, 2};
auto g = {1, 2.0};
参考答案
| 变量 | 结果 |
a | int |
b | std::initializer_list<int> |
c | double |
d | std::initializer_list<double> |
e | 编译错误 |
f | std::initializer_list<int> |
g | 编译错误,元素类型不一致 |
总结
可以简单记为:
直接花括号看单个元素,带等号花括号看初始化列表。