如何结合委托构造函数与条件初始化列表编写拷贝/移动构造函数
实现支持选择性构造成员的Result容器拷贝/移动构造函数
要解决这个问题,核心是手动控制union成员的构造时机,避免触发模板类型的默认构造要求。可以用C20的std::construct_at(或C17及之前的placement new),根据error_state_的值,只构造union中对应的成员。
核心实现代码
#include <memory> // 用于std::construct_at #include <utility> // 用于std::move template <typename ResultType, typename ErrorType> class Result { private: bool error_state_; union { ResultType result_; ErrorType error_; }; public: // 普通构造函数(示例) Result(ResultType res) : error_state_(false) { std::construct_at(&result_, std::move(res)); } Result(ErrorType err) : error_state_(true) { std::construct_at(&error_, std::move(err)); } // 拷贝构造函数 Result(const Result& other) : error_state_(other.error_state_) { if (error_state_) { std::construct_at(&error_, other.error_); } else { std::construct_at(&result_, other.result_); } } // 移动构造函数 Result(Result&& other) noexcept : error_state_(other.error_state_) { if (error_state_) { std::construct_at(&error_, std::move(other.error_)); } else { std::construct_at(&result_, std::move(other.result_)); } } // 析构函数:手动销毁对应成员 ~Result() { if (error_state_) { std::destroy_at(&error_); } else { std::destroy_at(&result_); } } // 拷贝赋值运算符(配套实现) Result& operator=(const Result& other) { if (this == &other) return *this; // 先销毁当前成员 if (error_state_) { std::destroy_at(&error_); } else { std::destroy_at(&result_); } // 再构造新成员 error_state_ = other.error_state_; if (error_state_) { std::construct_at(&error_, other.error_); } else { std::construct_at(&result_, other.result_); } return *this; } // 移动赋值运算符(配套实现) Result& operator=(Result&& other) noexcept { if (this == &other) return *this; // 先销毁当前成员 if (error_state_) { std::destroy_at(&error_); } else { std::destroy_at(&result_); } // 再构造新成员 error_state_ = other.error_state_; if (error_state_) { std::construct_at(&error_, std::move(other.error_)); } else { std::construct_at(&result_, std::move(other.result_)); } return *this; } // 核心访问接口 bool has_error() const { return error_state_; } ResultType& value() { return result_; } const ResultType& value() const { return result_; } ErrorType& error() { return error_; } const ErrorType& error() const { return error_; } };
关键说明
- 避免默认构造依赖:union成员不会自动初始化,通过
std::construct_at显式构造对应成员,完全不需要ResultType或ErrorType提供默认构造函数。 - 手动管理析构:union成员不会自动析构,必须在析构函数里根据
error_state_调用std::destroy_at销毁对应成员,防止内存泄漏。 - 赋值逻辑匹配构造:拷贝/移动赋值时,需要先销毁当前对象的成员,再构造新成员,逻辑和构造函数完全对应。
- C++17及之前兼容:如果无法使用C++20的
std::construct_at,可以替换为placement new和手动调用析构函数:// 拷贝构造中构造error_的示例 new (&error_) ErrorType(other.error_); // 移动构造中构造result_的示例 new (&result_) ResultType(std::move(other.result_)); // 销毁时直接调用析构函数 error_.~ErrorType();
为什么委托构造方案有问题
带标签的委托构造会强制先执行委托的构造函数,而编译器可能会因此要求union成员具备默认构造能力——但我们根本不需要默认构造。直接在拷贝/移动构造函数里显式控制成员构造,逻辑更清晰,也能彻底规避这个问题。
内容的提问来源于stack exchange,提问作者DWil
相关产品推荐
相关产品推荐

