为何无法用std::initializer_list对象初始化数组?二者有何差异?
一、为何无法用std::initializer_list对象初始化数组?
1. C风格数组(如int array2[5])
C++对C风格数组的初始化有严格限制:它只能用花括号包裹的直接初始化器列表完成初始化,不支持用std::initializer_list对象来赋值或初始化。这是因为C风格数组属于内置类型,其初始化规则是编译期确定的,需要直接的字面量或常量表达式级别的元素列表;而std::initializer_list是一个运行时对象,两者类型完全不兼容,编译器无法完成这种转换,因此报错“array must be initialized with a brace-enclosed initializer”。
2. std::array(如std::array<int,5> a2)
std::array虽然是标准容器,但本质是封装了C风格数组的聚合类型。聚合类型的初始化只能通过两种方式:
- 直接使用花括号初始化器列表进行聚合初始化(比如
a1 = {1,2,3,4,5}); - 用同类型的
std::array对象拷贝初始化。
和std::vector、std::set不同,std::array没有提供接受std::initializer_list参数的构造函数。当你尝试用il1初始化a2时,编译器试图把std::initializer_list<int>转换为std::array<int,5>,但不存在合法的转换路径,因此报错“conversion from 'std::initializer_list
二、std::initializer_list il1={1,2,3,4,5}中的il1和单独的{1,2,3,4,5}的区别
类型本质不同:
{1,2,3,4,5}不是一个具体类型,它是一个初始化器列表字面量,编译器会根据上下文将其解析为不同的用途:比如在std::vector<int> v={...}中,会被隐式转换为std::initializer_list<int>去调用vector的构造函数;在C风格数组初始化时,就是直接的元素初始化列表;在聚合初始化时,是给聚合成员赋值的列表。
而il1是明确的std::initializer_list<int>类型对象,它有固定的内存布局(包含指向底层数组的指针和元素数量),是可以被传递、拷贝的实体对象。生命周期不同:
单独的{1,2,3,4,5}作为初始化器时,其底层的临时数组生命周期仅在当前初始化表达式结束时;而il1作为命名对象,生命周期和普通变量一致,直到离开其作用域。注意:std::initializer_list的拷贝不会复制底层数组,只是拷贝指针和大小,所以多个std::initializer_list对象可能指向同一临时数组。使用场景不同:
{...}只能用于初始化场景,无法直接作为函数参数传递(除非函数参数是std::initializer_list,此时编译器会自动转换);而il1作为对象,可以自由传递给任何接受std::initializer_list参数的函数,或者赋值给其他同类型的std::initializer_list变量。
内容的提问来源于stack exchange,提问作者Crackie

