2016-07-29 73 views
15

考虑这个简单的类右值与拷贝操作

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引用),这使得副本分配成为m的后备当移动不可用时

为什么编译器后备时复制分配时常量左值引用可以绑定到右值和const Foo & f = getFoo();工程。

编译器 - gcc 4.7.2。

回答

10

没有后备,该概念被称为重载分辨率

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

注1: delete并不会逐字删除该方法。如果使用delete,则方法是定义为删除,但它仍可以通过重载解析找到。

cppreference.com文档(重点矿山):

... 重载首先发生,程序只是如果被删除的功能选择形成不良。

在您的例子中,移动构造函数可用(从视重载解析点)。但是,它被定义为删除

注意2:如果您不想让您的类具有移动构造函数,则不要定义它。如果您声明了以下其中一项,则编译器将不会生成移动构造函数:复制构造函数,复制赋值运算符,移动赋值运算符,析构函数。

+0

难道你不是指“移动构造函数*可用*”吗? – Quentin

+0

@Quentin你是对的,谢谢!编辑。 – sergej

8

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

如果您只是声明了复制赋值运算符,那么移动赋值运算符将不会被隐式声明,所以不会通过重载解析找到,而是会调用复制赋值运算符。

4

该报价可能有点误导。

如果由于明确声明了复制分配而未完全声明移动分配,则具有r值的调用将“回退”至复制分配。

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