rvalues与复制运算符

时间:2016-07-29 08:48:53

标签: c++ c++11

考虑这个简单的类

class Foo
{
    public:
    Foo() = default;
    Foo(const Foo &) = default;
    Foo & operator=(const Foo & rhs)
    {
        return *this;
    }
    Foo & operator=(Foo && rhs) = delete; 
};

Foo getFoo()
{
    Foo f;
    return f;
}

int main()
{
    Foo f;
    Foo & rf = f;
    rf = getFoo();    // Use of deleted move assignment.
    return 0;   
}

当我编译上面的例子时,我得到error: use of deleted function 'Foo& Foo::operator=(Foo&&)'

来自Copy Assignment

  

如果只提供了复制赋值,则所有参数类别都选择它(只要它按值获取参数或作为const的引用,因为rvalues可以绑定到const引用),这使得复制赋值成为移动赋值的后备,当移动不可用时。

当const lvalue引用可以绑定到rvalue并且const Foo & f = getFoo();有效时,为什么编译器不会回退到复制赋值。

编译器 - gcc 4.7.2。

3 个答案:

答案 0 :(得分:10)

没有后备,这个概念被称为重载决议

编译器执行重载决策并在检查方法是否被删除之前做出决定。 编译器决定移动构造函数是最佳选择,然后它确定已删除此方法,因此错误。

注1: delete不会直接删除该方法。如果使用delete,则该方法定义为已删除,但仍可通过重载解析找到。

cppreference.com文档(强调我的):

  

... 首先发生重载决策,如果选择了已删除的功能,该程序仅格式错误。

在您的示例中,移动构造函数可用(从重载解析的角度来看)。但是,被定义为已删除

注意2 :如果您不希望您的类具有移动构造函数,则只需不要定义它。如果您声明了以下内容之一,编译器将不会生成移动构造函数:复制构造函数,复制赋值运算符,移动赋值运算符,析构函数。

答案 1 :(得分:8)

编译器正在按照你的要求进行操作。您已删除移动分配运算符,表示您不希望允许从右值分配。移动分配操作符将在重载解析期间找到,并且由于它已被删除,因此会发出诊断信息。

如果您只是声明了复制赋值运算符,则不会隐式声明移动赋值运算符,因此不会通过重载解析找到它,而是会调用复制赋值运算符。

答案 2 :(得分:4)

引用可能有点误导。

如果由于显式声明了复制赋值而未完全声明移动赋值,则带有r值的调用将“回退”到复制赋值。

但是,您已明确声明了移动分配,并将其删除。因此,移动分配的声明“可用”,并解析为已删除的定义。