Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++ allow you to move classes containing objects with deleted move operations?

Why am I allowed to use std::move on a class which contains fields of a type with deleted move semantics (case 1), but am not allowed to use it on an instance of such a class (case 2)?

I understand case 2. I have explicitly deleted the move constructor, so I get an error if I try to move it. But I would expect that this would also be the case in case 1, where such class is also being moved.

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&&)' } 
like image 225
mike Avatar asked Feb 05 '17 11:02

mike


1 Answers

Note the difference between the two classes. For TestNonMovable (case 2), you explicitly declare the move constructor as delete. With TestNonMovable tnm2 = std::move(tnm); the deleted move constructor is selected in overload resolution and then cause the error.

For SomeClass (case 1), you don't explicitly declare the move and copy constructor. The copy constructor will be implicitly declared and defined, but the move constructor will be implicitly declared and defined as deleted because it has a non-movable data member. Note the deleted implicitly declared move constructor won't participate in overload resolution. With SomeClass sc2 = std::move(sc1);, the copy constructor is selected and then the code works fine. The rvalue reference returned from std::move could be bound to lvalue-reference to const (i.e. const SomeClass&) too.

The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue). (since C++14)

BTW: std::move itself is allowed in both cases. It doesn't perform the move operation, it just converts the argument to rvalue. The move operation happens at construction in both cases.

like image 109
songyuanyao Avatar answered Sep 29 '22 19:09

songyuanyao