C++ 列表初始化
一、列表初始化
1.1 出现原因
在C++98/03 中的对象初始化方法有很多种方法,这些不同的初始化方法,都有各自的适用范围和作用。最关键的是,这些种类繁多的初始化方法,没有一种可以通用所有情况。
1 | // 基于数组的初始化 |
1.2 统一的初始化
C++11标准引入了列表初始化,它使用大括号{}对任意类型对象进行初始化。
1 | // 内置类型初始化 |
1.2.1 自定义类型初始化
1 | class Point { |
1.3 initializer-list
std::initialzer-list是C++11引入的轻量级类模板,只提供begin、end以及size成员函数。编译器将{ }内的数据存放到std::array中,并构建initializer_list 对象引用这个array对象中的元素,但并不包含它们,拷贝一个 initializer_list 对象会生成另一个引用相同底层元素的对象,而不是创建它们的新副本,即浅拷贝。
C++11起,STL容器之所以能够支持列表初始化原因在于每个容器都提供了一个形参为initializer_list的构造函数。编译器会先构造一个initializer_list对象,然后调用对应容器中形参为initializer_list的构造函数。这个构造函数的逻辑非常简单,它们只需要调用initializer_list对象的begin和end 函数,循环对本对象进行初始化。
1 | Point p1(1,2); |
说明:自定义数据类型如果需要实现该功能,需要自己实现参数为initializer-list 的构造函数
1.3.1 initializer-list其它用途
initializer-list不仅可以用于自定义类型的列表初始化,也可以用于传递相同类型数据的集合,例如作为函数的形参
1 | void print(std::initializer_list<int> l) |
1.4 注意事项
1.4.1 Narrowing conversions(缩窄转换)
在 C++ 中,缩窄转换是一种不安全的类型转换,因为目标类型可能无法保存源类型的所有值。缩窄转换是在编写代码中稍不留意就会出现,而且它的出现并不一定会引发错误,甚至有可能连警告都没有,所以有时候容易被人们忽略,比如:
1 | int x = 2025; |
C++标准规定下列情况属于缩窄转换:
- 从浮点类型转换整数类型
- 从long double转换到double或float,或从double转换 到float,除非转换的值是
constexpr并且在目标类型的范围内 - 从整数类型转换到浮点类型,除非转换的值是
constexpr并且在目标类型的范围内 - 从整数类型转换为另一种整数类型,该整数类型不能表示原始类型的所有值,除非转换的值是 constexpr 并且其值可以精确存储在目标类型中
使用列表初始化,编译器对于缩窄转换会报编译错误(MSVC、clang)或警告(GCC)。
1 | int i{3.14}; // 存在缩窄转换 |
1.4.2 列表初始化的优先级
列表初始化既可以支持普通的构造函数,也能够支持以initializer_list为形参的构造函数。如果这两种构造函数同时出现在同一个类里,以initializer_list为形参的构造函数优先级更高。
1 | class Point |