Logo
Section 1 / 4

统一初始化使用 {}

在本节课中,我们将学习如何使用 {} 来初始化变量。

导入

统一初始化使用 {}

在本节课中,我们将学习如何使用 {} 来初始化变量。

C++11 中变量的初始化方式变得统一。对于统一初始化,我们需要使用 {} 括号。

{} 初始化始终适用。

直接初始化

变量可以不使用赋值运算符直接声明:

1std::string str{"my String"}; 2int i{2011};

拷贝初始化

{} 也支持使用 = 运算符进行拷贝初始化:

1std::string str = {"my String"}; 2int i = {2011};

两者的区别在于:直接初始化会直接调用类型的构造函数,而拷贝初始化则是先创建一个值,再隐式转换为目标类型。

防止截断

截断(更准确地说是截断转换)是指将数值从一种类型隐式转换为另一种类型。这可能导致精度丢失,可能非常危险。

下面的例子展示了传统方式初始化基本类型时可能出现的问题。

编译器会发出警告,但仍然执行隐式转换,导致数据丢失。

无论使用直接初始化还是赋值方式,问题都存在:

1#include <iostream> 2 3int main(){ 4 char c1(999); 5 char c2 = 999; 6 std::cout << "c1: " << c1 << std::endl; 7 std::cout << "c2: " << c2 << std::endl; 8 9 int i1(3.14); 10 int i2 = 3.14; 11 std::cout << "i1: " << i1 << std::endl; 12 std::cout << "i2: " << i2 << std::endl; 13}

程序输出显示了两个问题:

  • 整型字面量 999 超出了 char 类型的表示范围。
  • 双精度浮点字面量 3.14 无法完整表示为 int 类型。

而使用 {} 初始化则可以避免此类问题。

此代码将引发错误。

1#include <iostream> 2 3int main(){ 4 char c1{999}; 5 char c2 = {999}; 6 std::cout << "c1: " << c1 << std::endl; 7 std::cout << "c2: " << c2 << std::endl; 8 9 int i1{3.14}; 10 int i2 = {3.14}; 11 std::cout << "i1: " << i1 << std::endl; 12 std::cout << "i2: " << i2 << std::endl; 13}

现在,这个不符合标准的程序会被拒绝编译。

输出结果还取决于所使用的编译器。使用 GCC 6.1 及以上版本时,会报错;低于该版本的 GCC 仅会发出警告。

不信?可以使用在线编译器 https://gcc.godbolt.org/ 测试一下。

clang++ 编译器的行为更加一致可靠。因此这里给出一个简单建议:

编译程序时,让截断被视为错误。

可以通过添加编译选项 -Werror=narrowing,使得 GCC 4.8 及以上版本直接拒绝程序,而不是仅仅发出警告。

再看一个例子:

1#include <iostream> 2using namespace std; 3 4int main() { 5 char c1{97}; 6 char c2 = {97}; 7 std::cout << "c1: " << c1 << std::endl; 8 std::cout << "c2: " << c2 << std::endl; 9}

表达式 char c1{97} 不属于截断,因为 97 在 char 类型的取值范围内。同样,char c2 = {97} 也不属于截断。


让我们通过另一个例子来更深入理解 {} 初始化。

转换

示例:{} 在不同类型和结构上的用法