C++11中拷贝赋值与移动赋值运算符定义的编译歧义问题问询
为什么同时定义值传递和右值引用的赋值运算符会触发g++的歧义错误?
这个问题其实戳中了C++重载决议里一个容易混淆的细节,我来给你拆解清楚:
首先,你的理解有一半是对的:左值赋值时确实只会调用operator=(C rhs),因为左值无法绑定到C&&类型的参数,所以另一个重载会被直接排除,不会有问题。
但到了右值赋值(比如a = C()这种临时对象赋值),情况就不一样了——g++会认为两个重载都是同等匹配的候选,根本没法选,所以报歧义。
为什么会这样?
当你传入一个右值时,两个重载的匹配逻辑都是“精确匹配”:
- 对于
operator=(C rhs):右值可以直接初始化值传递的参数rhs(这一步会调用移动构造函数),编译器把这种同类型的初始化判定为精确类型匹配。 - 对于
operator=(C&& rhs):右值可以直接绑定到右值引用参数,这是精确的引用绑定匹配。
在g++的重载决议规则里,这两种匹配的优先级是完全相同的——没有哪一个比另一个更“合适”,所以编译器只能抛出歧义错误,把选择权扔给你。
怎么解决?
其实你根本不需要同时定义这两个版本!那个operator=(C rhs)的“拷贝并交换”写法,本身就已经完美支持左值和右值赋值了:
- 传入左值时,
rhs通过拷贝构造初始化,交换后原左值的资源会被rhs带着销毁; - 传入右值时,
rhs会自动用移动构造初始化,效率和单独写operator=(C&&)完全一样,甚至更简洁。
如果你非要保留两个重载(比如有特殊逻辑需要分开处理),那可以把值传递的版本改成const C& rhs的形式:
C& operator=(const C& rhs) { // 拷贝逻辑 return *this; } C& operator=(C&& rhs) { swap(*this, rhs); return *this; }
这时候,右值赋值会优先匹配operator=(C&&),左值匹配const C&版本,就不会有歧义了。但说实话,拷贝并交换的单版本写法已经足够优雅高效,完全没必要多写一个右值引用版本。
内容的提问来源于stack exchange,提问作者user3560270
相关产品推荐
相关产品推荐

