使用移动赋值和传值复制赋值的运算符=存在歧义重载问题。

13

如果我为类thing定义了一个使用传值方式调用复制构造函数的复制赋值运算符:

thing& operator= (thing x) {

为同一类添加移动赋值运算符:

thing& operator= (thing&& x) {

尝试调用移动赋值会导致gcc出现错误:
error: ambiguous overload for ‘operator=’ (operand types are ‘thing’ and ‘std::remove_reference<thing&>::type {aka thing}’)

然而,如果复制赋值运算符使用的是引用传递:
thing& operator= (thing& x) {

编译通过,两个操作符都可以调用。为什么呢?

C++11完整测试代码:

#include <iostream>
#include <utility>

using namespace std;

class thing {
    public:
        thing () { }
        thing (thing& x) {
            cout << "Copy constructor.\n";
        }
        thing (thing&& x) {
            cout << "Move constructor.\n";
        }
        thing& operator= (thing x) {
            cout << "Copy assign using pass by value.\n";
            return *this;
        }
        thing& operator= (thing&& x) {
            cout << "Move assign.\n";
            return *this;
        }
};


int main (void) {
    thing a, b;
// Invoke move assignment:
    a = move(b);
    return 0;
}                        

2
在重载决议中有一个特殊规则,即类型为A的右值更倾向于绑定到A&&而不是A&。对于A&&A之间没有相应的规则。 - T.C.
@T.C. A& 不必须是 lvalue 吗? - CodeClown42
实际上,是的,我应该写成 const A&A& 只绑定左值。 - T.C.
1个回答

5
  • 调用move(b)返回一个rvalue

  • 两个重载函数operator=(thing &&x)operator=(thing x)都可以接受rvalues作为输入。

  • 因此,这两个重载函数会产生二义性,编译器有理由抱怨,因为它无法在它们之间进行选择。


+1 我理解第二点(有关rvalue作为输入),但第一点的相关性是什么? - CodeClown42
1
@goldilocks,模糊的调用在a = move(b);处,这与调用a.operator=(move(b))相同。std::move返回一个引用其输入(即b)的rvalue引用。编译器看到std::move的返回值是一个rvalue引用,并在重载决议期间尝试找到哪个重载适合以std::move返回的结果作为输入。不幸的是,正如@T.C已经评论的那样,没有规则可以区分这两个重载。因此,您会收到一个模糊的调用错误。 - 101010
6
这个问题有解决方案吗? - Brady Dean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接