为什么C++允许您移动包含已删除移动操作的对象的类?

mik*_*ike 29 c++ language-lawyer move-semantics c++11 c++14

为什么我允许std::move在包含具有已删除移动语义的类型的字段的类上使用(情况1),但是不允许在这样的类的实例上使用它(情况2)?

我理解案例2.我已经明确删除了移动构造函数,所以如果我试图移动它,我会收到错误.但我希望在案例1中也会出现这种情况,这样的类也在被移动.

class TestNonMovable {
  std::string ss;
 public:
  TestNonMovable(){}
  TestNonMovable(const TestNonMovable&) {}
  TestNonMovable(TestNonMovable&&) = delete;
};

class SomeClass {
  TestNonMovable tnm;
};

int main() {    
    // case1: This compiles, my understanding is that for SomeClass::tnm compiler will use copy constrctor
    SomeClass sc1;
    SomeClass sc2 = std::move(sc1);

    // case2: This does not compile, move constructor is explicitly deleted, compiler will not try using copy constructor
    TestNonMovable tnm;
    TestNonMovable tnm2 = std::move(tnm); //error: use of deleted function 'TestNonMovable::TestNonMovable(TestNonMovable&&)'
}
Run Code Online (Sandbox Code Playgroud)

son*_*yao 45

注意两个类之间的区别.对于TestNonMovable(情况2),您显式地将移动构造函数声明为delete.随着TestNonMovable tnm2 = std::move(tnm);被删除的举动构造函数重载决策选择,然后会导致错误.

对于SomeClass(情况1),您没有显式声明移动和复制构造函数.复制构造函数将被隐式声明和定义,但移动构造函数将被隐式声明并定义为已删除,因为它具有不可移动的数据成员.请注意,已删除的隐式声明的移动构造函数不会参与重载解析.使用SomeClass sc2 = std::move(sc1);,复制构造函数被选中,然后代码工作正常.返回的rvalue引用std::move也可以绑定到对const(也const SomeClass&)的lvalue-reference .

重载解析会忽略已删除的隐式声明的移动构造函数(否则会阻止rvalue的复制初始化).(自C++ 14起)

BTW:在两种情况下都允许使用std :: move本身.它不执行移动操作,只是将参数转换为rvalue.在两种情况下,移动操作都发生在施工中.

  • @CarstenS由于[缺陷解决方案](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1402)引入了此更改,因此实现了他们应用此规则的权利在C++ 11模式中,Clang和GCC都这样做. (12认同)