使用工会来防止破坏?

Using a union to prevent destruction?

相关:How does =delete on destructor prevent stack allocation?

首先,我意识到这种事情是在玩火,诱惑UB。我要求更好地理解 unions.

的极端情况

据我了解,如果我有一个带有 c'tor 和 d'tor 的 class 并将其放在 union 中,编译器将强制我告诉它该做什么关于建设和破坏。如

#include <iostream>

struct S {
    int x;
    S() : x(-1) { std::cout << "S()" << std::endl; }
    ~S() { std::cout << "~S()" << std::endl; }
    static S& instance() {
        union DoNotDestruct {
            S value;
            DoNotDestruct() : value() {}
            ~DoNotDestruct() {}
        };
        static DoNotDestruct inst;
        return inst.value;
    }
};

int main() {
    return S::instance().x;
}

只报告建造,不报告破坏。

但是,如果我有一个 deleted 析构函数,那似乎不起作用;我必须使用 placement-new:

#include <new>

// Placement-new version:
struct S0 {
    S0() {}
    ~S0() = delete;
    static S0& instance() {
        union DoNotDestruct {
            S0 value;
            DoNotDestruct() {
                // I believe value isn't constructed yet.
                new (&value) S0; // Placement-new.
            }
            ~DoNotDestruct() {} // Omit S0's d'tor.
        };
        static DoNotDestruct inst;
        return inst.value;
    }
};

// Init version:
struct S1 {
    S1() {}
    ~S1() = delete;
    static S1& instance() {
        union DoNotDestruct {
            S1 value;
            DoNotDestruct() : value{} {} // Why does this line want S1::~S1() to exist?
            ~DoNotDestruct() {} // Omit S1's d'tor.
        };
        static DoNotDestruct inst;
        return inst.value;
    }
};

https://godbolt.org/z/7r4ebszor

在这里,S0 很高兴,但是 S1 在构造函数行上抱怨 S1::~S1() 被删除了。为什么?

我尝试添加 noexcept 的想法是,在 class 中,如果您有多个成员,如果任何后续 c'tor 抛出,第 0 个成员需要是可破坏的。看起来即使是删除了 d'tor 的 class 本身也不能拥有坚不可摧的成员,即使 c'tor 都是 noexcept。它给出与 union 相同的错误:https://godbolt.org/z/jx3W1YEPf

[class.dtor]/15 表示如果 可能调用的 析构函数被定义为已删除,则程序格式错误。

[class.base.init]/12 表示 可能构造的 class 的子对象 的析构函数可能在非委托构造函数中调用。

[special]/7 表示所有非静态数据成员都是潜在构造的子对象,虽然我认为这句话限定有点奇怪(对于 class、[...]").

对于单成员 classes、工会或 noexcept,我没有看到任何例外情况。所以在我看来 GCC 对于 S1.

是正确的

Clang 也是 Clang、GCC、ICC 和 MSVC 中唯一不拒绝此版本的编译器。

然而,对于 S0,此推理也适用,因此它的格式也不正确。然而,四个编译器中的 none 同意这一点,所以也许我错了。


引用的措辞主要来自CWG issue 1424. Clang lists its implementation status currently as unknown (https://clang.llvm.org/cxx_dr_status.html), as does GCC (https://gcc.gnu.org/projects/cxx-dr-status.html)。