考虑这个简单的类
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&&)'
如果只提供了复制赋值,则所有参数类别都选择它(只要它按值获取参数或作为const的引用,因为rvalues可以绑定到const引用),这使得复制赋值成为移动赋值的后备,当移动不可用时。
当const lvalue引用可以绑定到rvalue并且const Foo & f = getFoo();
有效时,为什么编译器不会回退到复制赋值。
编译器 - gcc 4.7.2。
答案 0 :(得分:10)
没有后备,这个概念被称为重载决议。
编译器执行重载决策并在检查方法是否被删除之前做出决定。 编译器决定移动构造函数是最佳选择,然后它确定已删除此方法,因此错误。
注1:
delete
不会直接删除该方法。如果使用delete
,则该方法定义为已删除,但仍可通过重载解析找到。
从cppreference.com文档(强调我的):
... 首先发生重载决策,如果选择了已删除的功能,该程序仅格式错误。
在您的示例中,移动构造函数可用(从重载解析的角度来看)。但是,被定义为已删除。
注意2 :如果您不希望您的类具有移动构造函数,则只需不要定义它。如果您声明了以下内容之一,编译器将不会生成移动构造函数:复制构造函数,复制赋值运算符,移动赋值运算符,析构函数。
答案 1 :(得分:8)
编译器正在按照你的要求进行操作。您已删除移动分配运算符,表示您不希望允许从右值分配。移动分配操作符将在重载解析期间找到,并且由于它已被删除,因此会发出诊断信息。
如果您只是声明了复制赋值运算符,则不会隐式声明移动赋值运算符,因此不会通过重载解析找到它,而是会调用复制赋值运算符。
答案 2 :(得分:4)
引用可能有点误导。
如果由于显式声明了复制赋值而未完全声明移动赋值,则带有r值的调用将“回退”到复制赋值。
但是,您已明确声明了移动分配,并将其删除。因此,移动分配的声明“可用”,并解析为已删除的定义。