You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合委托构造函数与条件初始化列表编写拷贝/移动构造函数

实现支持选择性构造成员的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_; }
};

关键说明

  1. 避免默认构造依赖:union成员不会自动初始化,通过std::construct_at显式构造对应成员,完全不需要ResultType或ErrorType提供默认构造函数。
  2. 手动管理析构:union成员不会自动析构,必须在析构函数里根据error_state_调用std::destroy_at销毁对应成员,防止内存泄漏。
  3. 赋值逻辑匹配构造:拷贝/移动赋值时,需要先销毁当前对象的成员,再构造新成员,逻辑和构造函数完全对应。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 12:55:11