聚合初始化时类内成员初始化顺序:标准是否规定i晚于v初始化?
聚合初始化中类内成员初始化的执行顺序问题
先看这段代码:
#include <iostream> #include <print> #include <span> #include <vector> struct Entry { std::vector<char> v; std::vector<char>::iterator i = v.begin(); }; int main() { std::vector<char> v{'a', 'b', 'c', 'd', 'e'}; Entry e{.v = std::move(v)}; std::print(std::cout, "{}", std::span(e.i, e.v.end())); }
这段代码运行后会输出['a', 'b', 'c', 'd', 'e']。有人可能误以为Entry e{.v = std::move(v)};的逻辑类似先默认构造e再给e.v赋值,会导致e.i失效,但实际并非如此——它的执行逻辑更接近先初始化e.v,再用初始化后的e.v来初始化e.i。
核心问题解答
是的,聚合初始化过程中,类内初始化语句.i = v.begin()必然在v完成初始化之后执行,C++标准对此有明确规定。
具体规则说明
根据C标准(以C20为例,对应条款§9.4.2 [dcl.init.aggr]):
- 聚合初始化时,成员的初始化顺序严格遵循它们在类定义中的声明顺序,完全不受初始化列表中指定的顺序影响;
- 对于初始化列表中没有指定初始化的成员,会使用其类内初始化器完成初始化,这一步会在所有前面声明的成员都完成初始化后执行。
对于Entry结构体,v的声明早于i:
- 首先执行初始化列表中的
.v = std::move(v),完成e.v的移动构造初始化; - 接着执行
i的类内初始化器v.begin()——此时v已经是完全初始化后的有效容器,因此i会指向e.v的起始位置,是有效的迭代器。
这和“默认构造Entry后再给v赋值”的场景完全不同:后者是先默认构造v(空容器),此时i会指向空容器的起始迭代器;之后给v赋值会导致容器重新分配内存,原来的迭代器i失效。而聚合初始化是直接按声明顺序初始化成员,不会出现迭代器失效的问题。
内容的提问来源于stack exchange,提问作者3CEZVQ
相关产品推荐
相关产品推荐

