文章
C++中”=初始化变量“和”{}初始化变量“的区别
目录
- 在 C++11 以后,引入了“统一初始化”(uniform initialization),也就是用一对花括号 { … } 来初始化所有类型。下面分几点来对比这两种写法的异同:
- 1. 语法与分类
- char mychar = 'a'; int myint = 123; double mydouble = 456.7; - 使用 `=` 号,是最传统的初始化方式。 - 本质上,编译器会尝试将右侧的值“转换”成目标类型,然后拷贝/赋值。 2. **列表初始化(list initialization)/直接列表初始化(direct list initialization)** c++ char mychar { 'a' }; int myint { 123 }; double mydouble{ 456.78 }; ``` - 用 {} 包裹,即可用于内置类型、类类型、聚合类型、STD 容器等。 - 有时也称为“花括号初始化”或“统一初始化”。
- 2. 最主要的区别:禁止窄化转换(narrowing conversions)
- 3. 对构造函数和 initializer_list 的影响
- 4. 避免 “最令人讨厌的解析”(Most Vexing Parse)
- 使用花括号就能彻底避免这种二义性。
- 5. 性能和生成代码
- 6. 总结
在 C++11 以后,引入了“统一初始化”(uniform initialization),也就是用一对花括号 { … } 来初始化所有类型。下面分几点来对比这两种写法的异同:#
1. 语法与分类#
- 拷贝初始化(copy initialization)
char mychar = 'a';
int myint = 123;
double mydouble = 456.7;
- 使用 `=` 号,是最传统的初始化方式。 - 本质上,编译器会尝试将右侧的值“转换”成目标类型,然后拷贝/赋值。 2. **列表初始化(list initialization)/直接列表初始化(direct list initialization)** c++
char mychar { 'a' };
int myint { 123 };
double mydouble{ 456.78 };
```
- 用 {} 包裹,即可用于内置类型、类类型、聚合类型、STD 容器等。
- 有时也称为“花括号初始化”或“统一初始化”。#
2. 最主要的区别:禁止窄化转换(narrowing conversions)#
- 拷贝初始化 会进行隐式转换,允许从一个类型转换到另一个类型,甚至发生精度丢失或溢出。
double d = 3.14; int i = d; // 隐式窄化:i == 3,编译器通常只给 Warning(或无提示)
```
- 列表初始化 禁止任何窄化转换,编译期报错:
double d = 3.14; int i{ d }; // error: narrowing conversion of 'd' from 'double' to 'int'
```
这条规则让初始化更安全,能在编译期捕获精度丢失或溢出问题。
3. 对构造函数和 initializer_list 的影响#
对于自定义类型,列表初始化还会影响重载解析:
struct A {
A(int); // (1)
A(std::initializer_list<int>); // (2)
};
A a1{ 1 }; // 调用 (2) 还是 (1) ?
- 如果存在接受
std::initializer_list的构造,列表初始化优先匹配initializer_list构造函数 (2)。 - 而
A a2(1);(圆括号)则直接匹配 (1)。
4. 避免 “最令人讨厌的解析”(Most Vexing Parse)#
老式圆括号或等号初始化,有时会被编译器误解析为函数声明。
std::vector<int> v(); // “声明”了一个返回 vector<int> 的函数 v!
std::vector<int> v{}; // 明确是创建一个空 vector<int> 变量
使用花括号就能彻底避免这种二义性。#
5. 性能和生成代码#
- 对于内置类型,二者性能、生成的机器码基本相同。
- 对于类类型,拷贝初始化可能多一次“临时对象 + 拷贝/移动”,而列表/直接初始化通常更直接。但现代编译器也会做优化(如返回值优化、拷贝消除)。
6. 总结#
| 特性 | `=` 拷贝初始化 | `{}` 列表/直接初始化 |
| **允许窄化转换** | 是(隐式转换) | 否(编译期报错) |
| **优先匹配 ****`initializer_list`** | 否 | 是 |
| **避免 Most Vexing Parse** | 有二义性风险 | 无二义性 |
| **适用范围** | 内置类型、类类型、聚合类型 | 统一,所有类型 |
建议:在现代 C++ 中,如果不需要接口兼容旧代码,优先使用 {} 列表初始化,以获得更严格、更安全的类型检查和更一致的语义。