持有删除拷贝构造的对象引用的类如何实现移动赋值运算符?
问题解答
问题1:报错处理方案
核心根本原因:你代码中的mThing是左值引用类型,C++规定引用一旦完成初始化就无法重新绑定,你写的mThing = other.mThing并不是修改引用的绑定目标,而是对mThing指向的MoveOnlyThing对象执行赋值操作,自然触发了被删除的拷贝赋值报错。
根据你的实际设计需求,有两种解决路径:
- 场景1:移动
Holder时仅需要转移对MoveOnlyThing对象的绑定关系,不需要操作对象本身的赋值
这种场景不能使用引用作为成员,必须替换为指针类型(只有指针支持修改指向目标),修改后的代码示例:class Holder { public: Holder(MoveOnlyThing& t) : mThing(&t) { } Holder(Holder&& other) noexcept : mThing(other.mThing) { other.mThing = nullptr; // 可选,按需决定是否置空原Holder的指针 } Holder& operator=(Holder&& other) noexcept { if (this != &other) { mThing = other.mThing; other.mThing = nullptr; } return *this; } private: MoveOnlyThing* mThing = nullptr; // 可对外提供返回引用的getter隐藏指针实现 // MoveOnlyThing& getThing() { return *mThing; } }; - 场景2:移动
Holder时确实需要将源Holder绑定的MoveOnlyThing对象移动赋值到当前Holder绑定的对象
需要做两处修改:- 给
MoveOnlyThing添加移动赋值运算符:你显式删除了拷贝赋值运算符后,编译器不会自动生成移动赋值运算符,需要手动声明:class MoveOnlyThing { public: MoveOnlyThing() = default; MoveOnlyThing(const MoveOnlyThing&) = delete; MoveOnlyThing& operator=(const MoveOnlyThing&) = delete; // 新增移动赋值运算符,可default或自定义实现 MoveOnlyThing& operator=(MoveOnlyThing&&) = default; }; - 修改
Holder的移动赋值代码,将other.mThing转为右值触发移动赋值:Holder& operator=(Holder&& other) { mThing = std::move(other.mThing); return *this; }
- 给
问题2:引用赋值触发拷贝操作的原理
C++中的引用本质是对象的别名,没有独立的存储空间,一旦在构造阶段完成绑定,后续所有对引用的操作都会直接作用在它绑定的对象上,不存在任何语法可以修改引用的绑定目标。
你代码中mThing = other.mThing的实际执行逻辑等价于:对当前Holder的mThing引用绑定的MoveOnlyThing实例,执行赋值运算符,传入参数是other.mThing引用绑定的MoveOnlyThing实例。
因为other.mThing是左值,重载解析会优先匹配参数为const MoveOnlyThing&的拷贝赋值运算符,而你已经显式删除了该函数,所以编译器直接抛出错误。
额外说明:你的移动构造没有报错是因为移动构造处于对象初始化阶段,成员初始化列表中的mThing(other.mThing)是在做引用绑定,而非赋值操作,不会调用任何MoveOnlyThing的赋值函数。
内容的提问来源于stack exchange,提问作者yano
相关产品推荐
相关产品推荐

