Que*_*tin 13 c++ placement-new object-lifetime language-lawyer
当心,我们正在绕过龙的巢穴.
考虑以下两个类:
struct Base {
std::string const *str;
};
struct Foo : Base {
Foo() { std::cout << *str << "\n"; }
};
Run Code Online (Sandbox Code Playgroud)
如您所见,我正在访问未初始化的指针.还是我?
让我们假设我只使用Base那些微不足道的类,只不过是(可能是嵌套的)指针.
static_assert(std::is_trivial<Base>{}, "!");
Run Code Online (Sandbox Code Playgroud)
我想Foo分三步构建:
为a分配原始存储空间 Foo
Base通过placement-new 初始化一个适当放置的子对象
Foo通过placement-new 构建.
我的实现如下:
std::unique_ptr<Foo> makeFooWithBase(std::string const &str) {
static_assert(std::is_trivial<Base>{}, "!");
// (1)
auto storage = std::make_unique<
std::aligned_storage_t<sizeof(Foo), alignof(Foo)>
>();
Foo * const object = reinterpret_cast<Foo *>(storage.get());
Base * const base = object;
// (2)
new (base) Base{&str};
// (3)
new (object) Foo();
storage.release();
return std::unique_ptr<Foo>{object};
}
Run Code Online (Sandbox Code Playgroud)
既然Base是微不足道的,我的理解是:
跳过Base构造的琐碎的析构函数(2)很好;
Base作为Fooat的一部分构造的子对象的普通默认构造函数(3)不执行任何操作;
因此Foo接收一个初始化指针,一切都很好.
当然,这就是在实践中发生的事情,即使在-O3(请亲自看看!).
但它是安全的,还是龙有一天会抓住并吃掉我?
标准似乎明确禁止这一点.除非它是基类,否则显式允许结束对象生存期并在同一位置启动新对象生存期 :
§3.8对象生命周期
§3.8.7 - 如果在对象的生命周期结束之后,在重用或释放对象占用的存储之前,在原始对象占用的存储位置创建一个新对象,指向原始对象的指针object,引用原始对象的引用,或者原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操作新对象,如果:
新对象的存储完全覆盖原始对象占用的存储位置,以及
新对象与原始对象的类型相同(忽略顶级cv限定符),和
[snip]和
原始对象是类型为T的派生程度最高的对象(1.8),新对象是类型为T的派生程度最高的对象(也就是说,它们不是基类子对象).
| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |