C++17中使用placement-new显式创建平凡默认构造函数对象
修改后的make_uninitialized_x_fixed_for_cpp17函数返回的指针,在C++17及更早版本中写入其指向对象的成员是合法的,核心原因如下:
C++17及之前的对象生命周期规则:C++20之前没有「隐式生命周期对象的隐式创建」机制,
std::malloc分配的只是一块原始内存字节,内存中不存在任何X类型的对象。此时直接通过make_uninitialized_x返回的指针访问X的成员,属于未定义行为(UB)——因为编译器无法基于一个不存在的对象来保证内存访问的合法性。placement-new的关键作用:
new (p) X;这个placement-new表达式会在p指向的malloc内存中显式创建一个X对象。在C17中,这个操作会触发X的默认初始化(对于聚合类型X,成员a和b会保持未初始化状态,和malloc后的内存状态一致),但核心是:这个表达式完成了X对象的构造,内存中从此存在一个合法的X实例,后续对其成员的读写操作完全符合C标准要求,不再是未定义行为。关于优化消除的说明:你提到
-O1优化时这个placement-new会被消除——需要明确的是:编译器可能会优化掉初始化操作(比如如果后续代码没有读取成员值,默认初始化的动作可能被省略),但对象的创建本身是不能被优化消除的。只要执行了placement-new表达式,X对象的生命周期就已经开始,后续的成员访问就是合法的。
如果你的需求是仅创建对象但完全跳过成员初始化(保持malloc后的原始内存状态),在C++17中new (p) X;已经满足这个需求(因为X是聚合类型,默认初始化不会修改成员值);如果是非聚合类型且有默认构造函数,这个写法会调用默认构造函数,此时若想跳过初始化需要使用更底层的操作,但你的场景中X是聚合类型,当前写法已经足够。
内容的提问来源于stack exchange,提问作者StableGeneous

