跳转至

列表初始化

基本语法

在 C 中我们经常见到如下方式的数组初始化方式和结构体初始化方式:

int a[4] = { 1, 2, 3, 4 };
struct Point
{
    int x,y;
};
struct Point b = { 1, 2 };

这个大括号括起来的列表就被用作初始化数组 a 和结构体 b,而 C++11 中扩大了该列表的适用范围,使其可以初始化所有内置类型和用户自定义类型,并且在 C++11 中, = 也可被省略,如下:

// 内置类型
int a[4] { 1, 2, 3, 4 };
double b = { 4.91 };
int c { 4 };

// 用户自定义类型
class Date
{
    int Y,M,D;
public:
    Date(int y,int m,int d):Y(y),M(m),D(d){}
};
Date a { 2024, 4, 6 };

对于用户自定义类型,列表初始化会自动调用参数匹配的构造函数。

缩窄检查

我们使用一般的方式对数据进行初始化时,编译器允许程序员执行下面的操作:

char a = 67.75;
char b = 1000000;

在使用 {} 对变量进行初始化时,可以防止类型缩窄,即禁止将数值赋值给无法存储它的变量,并且使用类型不匹配的变量初始化时也会报 Warning,如下:

char a1 = { 1000000 }; // compile error:from 'int' to 'char' inside { }
char a2 = { 67.775 }; // compile error:from 'double' to 'char' inside { }

double b = 67.775;
char c = { b }; // Warning:from 'double' to 'char' inside { }

{} 初始化允许类型转换为更宽的类型。另外只要在较窄类型的范围内,类型转换也是允许的。

double a = { 1 }; // int to double
char b = { 66 }; // int to char , in range

std::initializer_list

C++11 提供了模板类 std::initializer_list ,它是一个类似数组的容器,列表中的元素必须是同一类型,通过下面不定个数参数求和的例子理解 std::initializer_list

# include <iostream>
int sum(std::initializer_list<int> li)
{
    int s = 0;
    for(auto it = li.begin();it != li.end();it++)
        s += *it;
    return s;
}

int main()
{
    std::cout << sum({ 1, 2, 3, 4 }) << '\n';
    return 0;
}
// 输出: 10

当一个类含有接受 std::initializer_list 为参数的构造函数时,则列表初始化语法就只能用于调用该构造函数。C++11 中 STL 容器都提供了 std::initializer_list 为参数的构造函数,所以我们可以像构造数组一样构造容器:

std::vector<int> vec { 1, 2, 3, 4, 5 };
std::list<int> li { 1, 2, 3, 4, 5 };
std::map<int,int> mp { { 1, 2 }, { 3, 4 } };