【发布时间】:2020-11-22 08:41:25
【问题描述】:
我想将我的成员变量的构造推迟到构造函数的主体,我正在尝试使用联合来做到这一点。到目前为止,它实现了我想要的,但我想问有什么理由我不应该这样做?
例子:
#include <iostream>
struct A {
A() {
std::cout << "Construct A" << std::endl;
}
~A() {
std::cout << "Destruct A" << std::endl;
}
};
struct B {
A a;
};
template <typename T>
union U {
char a{};
T buffer;
U() {}
~U() {
buffer.~T();
}
};
struct C {
U<B> u;
C() {
try {
new (&u.buffer) B();
} catch (...) {
}
}
};
编辑:添加示例用法
【问题讨论】:
-
我看到的最直接的问题是它令人困惑。我认为
chars 的数组就是sizeofA将是要走的路,然后在B的构造函数主体中使用operator new。这也能达到你想要的吗?另外,我确定您将施工推迟到尸体有效之前的原因,但是您介意为我们这些后排的人分享它们吗? -
但是我不能使用像
u.b这样的东西来保证类型安全 -
U的析构函数不应该做任何事情。如果您只是在u被销毁之前以某种方式最终没有初始化u.buffer,那么这使得获得UB 变得非常容易。如果u.buffer的破坏在C::~C()中,我会感觉更好。例如。如果B::B()像现在这样输入这段代码,我认为会有 UB。否则,我认为它可能没问题。 -
@UyHà 如果
B::B()抛出,那么C::C()将捕获并忽略异常。但是,B对象u.buffer不会开始它的生命周期,因为它不是构造的。到目前为止,这很好。现在,最终C将被销毁。在C::~C()被调用之后,u.~U()将被调用(你无法阻止它!)。u.~U()会尝试调用u.buffer.~B(),但是没有buffer可以销毁。那是UB。一种解决方法是U<B>::~U()什么都不做,C::~C()在需要时做u.buffer.~B()。std::optional等实用程序封装了该逻辑。 -
不,它仍然是 UB。一旦构造函数体开始执行,所有的成员变量都被初始化了。如果构造函数因异常退出,这些成员仍将被销毁。这也调用了
u.~U(),然后是u.buffer.~B(),如果你没有成功初始化u.buffer,这将是UB。在这种情况下,我认为@yao99 的答案是一个好主意:完全摆脱U,只需使用函数-try来检测成员初始化程序中的故障。基本上,一旦你点击了C::C()的{,你必须以某种方式初始化u.buffer以避免UB,这可能很困难。
标签: c++ initialization union destructor