使用 union 推迟成员变量构造

Use union to defer member variable construction

我想将成员变量的构造推迟到构造函数的主体,我正在尝试使用 union 来执行此操作。到目前为止,它正在实现我想要的,但我想问一下我有什么理由不应该这样做吗?

示例:

#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 (...) {
    }
  }
};

编辑:添加示例用法

如果您使用的是 C++17,

std::optional 似乎是执行此操作的好方法。

#include <iostream>
#include <optional>
#include <stdexcept>

struct A {
    A(bool fail = false) {
        std::cout << "Attempting to construct A" << std::endl;
        if (fail) {
            throw std::runtime_error("Failed to construct A");
        }
        else {
            std::cout << "Succeeded in constructing A" << std::endl;
        }
    }

    ~A() {
        std::cout << "Destruct A" << std::endl;
    }
};

struct B {
    std::optional<A> a;

    B(bool fail = false) {
        try {
            a.emplace(fail);
        }
        catch (std::runtime_error& ex) {
            // fall back to a safe construction
            std::cout << "Falling back to safe A construction" << std::endl;
            a.emplace();
        }
    }
};

int main() {
    {
        B b_good; // should be fine
    }

    {
        B B_bad(true); // should catch the exception and fall back
    }
}

输出:

Attempting to construct A
Succeeded in constructing A
Destruct A
Attempting to construct A
Failed to construct A

放弃 std::optional 大小的一个选项是拥有未分配的缓冲区,但(为了类型安全)通过引用访问它。

#include <iostream>
#include <optional>
#include <stdexcept>

struct A {
    A(bool fail = false) {
        std::cout << "Attempting to construct A" << std::endl;
        if (fail) {
            throw std::runtime_error("Failed to construct A");
        }
        else {
            std::cout << "Succeeded in constructing A" << std::endl;
        }
    }

    ~A() {
        std::cout << "Destruct A" << std::endl;
    }
};

struct B {
    char a_buff_[sizeof(A)];
    A& a_;

    B(bool fail = false) : a_(*reinterpret_cast<A*>(a_buff_)) {
        try {
            new (&a_) A(fail);
        }
        catch (std::runtime_error& ex) {
            std::cout << ex.what() << std::endl;
            std::cout << "Falling back to safe A construction" << std::endl;
            new (&a_) A();
        }
    }

    ~B() { a_.~A(); }

    B(const B& other) : a_(other.a_) {}

    B& operator=(const B& other) {
        a_ = other.a_;
    }
};

int main() {
    {
        B b_good; // should be fine
    }
    
    {
        B b_bad(true); // should catch the exception and fall back
    }
}
Attempting to construct A
Succeeded in constructing A
Destruct A
Attempting to construct A
Failed to construct A
Falling back to safe A construction
Attempting to construct A
Succeeded in constructing A
Destruct A

您不应使用变通方法的原因之一是它没有意义。 在孔构造函数上应用 try-catch 会很好。

struct C {
    A a;
    C() try {
    } catch (...) {
    }
};