带引用成员的结构体为何无法使用默认移动赋值运算符?
先看示例代码:
struct MyFoo { MyFoo(int& arg) : ref(arg) {} int& ref; MyFoo& operator= (MyFoo&& other) = default; MyFoo& operator= (const MyFoo& other) = default; }; int main() { int a; MyFoo foo{a}; MyFoo foo2{a}; foo2 = std::move(foo); }
编译器提示
GCC编译提示:
MyFoo& operator= (MyFoo&& other) = default; is implicitly deleted
Clang编译提示(翻译后):
警告:显式默认的移动赋值运算符被隐式删除 [-Wdefaulted-function-deleted]
问题1:为什么显式默认移动赋值运算符会失败?
C++标准明确规定:当类包含引用类型成员时,默认的移动赋值运算符会被隐式删除。
原因在于,引用的本质是对象的别名,一旦在初始化时绑定到某个对象,就永远无法更改其指向(即不能“重绑定”)。默认移动赋值运算符的设计逻辑是对成员执行“移动”操作,但移动的核心是转移资源所有权——而引用本身不持有任何资源,只是绑定到已有对象,不存在可转移的所有权。
当你显式声明= default时,编译器会尝试按照标准规则生成默认版本,但由于引用成员的存在违反了移动赋值的前提条件,因此只能将该运算符标记为删除状态。
问题2:为什么默认生成的运算符不执行this->ref = other.ref;这类成员级赋值,而是直接被删除?
你手动实现的赋值运算符,本质是对引用指向的对象赋值,而非操作引用本身。但默认赋值运算符(无论复制还是移动)的设计逻辑是对成员变量本身进行复制/移动操作,而非成员变量指向的对象。
对于引用成员来说,“复制/移动成员”意味着尝试将一个引用的绑定关系转移到另一个引用上,但这违反了引用不可重绑定的核心特性。默认生成的赋值运算符不会去间接操作引用指向的对象——它只会严格按照“成员级操作”的规则执行,而引用成员无法满足这个规则,因此编译器只能删除该运算符。
简单总结:默认赋值操作的是成员变量本身,而你手动实现的是操作成员指向的对象,两者逻辑完全不同。标准规定默认赋值必须遵循成员级操作的规则,引用成员不满足这个要求,所以默认运算符被删除。
内容的提问来源于stack exchange,提问作者Zebrafish

