Move object without a move constructor

4.2k Views Asked by At

One more time about this, but the related questions do not answer my question.

The standard is pretty clear:

12.8 Copying and moving class objects,
§9
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator,
— X does not have a user-declared destructor, and
— the move constructor would not be implicitly defined as deleted.
[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. — end note ]

So, before noticing the "Note" at the end, I expected this piece of code to fail compilation (although I knew, that moving should fallback to copying):

#include <iostream>
using std::cout;
class c
{
public:
    c() { cout << "c::c()\n"; }
    c( std::initializer_list< int > ) { cout << "c::c( std::initializer_list )\n"; };

    c( const c& ) { cout << "c::c( const c& )\n"; }
    c& operator=( const c& ) { cout << "c& c::operator=( const c& )\n"; return *this; }

    ~c() { cout << "c::~c()\n"; }

    void f() {}
};

void f( c&& cr ) { cout << "f()\n"; cr.f(); }

int main()
{
    c x;
    f( std::move( x ) );

    return 0;
}

Then I saw the note at the end, but I was still surprised, that the code above outputs:

c::c()
f()
c::~c()

Note the "missing" c::c( const c& ). Then I added

c( c&& ) = delete;
c& operator=( c&& ) = delete;

and the result is still the same.

What do I miss here?


$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609

Compiler flags: -s -O0 -march=native -pthread -std=c++11 -Wall -Wextra -DNDEBUG.

2

There are 2 best solutions below

0
On BEST ANSWER

The cr parameter from your example is a r-value reference. The reference word is significant here. It acts somewhat similar to a plane old reference that we know from c++, before c++11 was in a game, in a terms that it does not invoke any constructor... it is just "pointing" to the object you pass...

To invoke the moving constructor (or do other swap) and consume the reference we need to forward the reference further e.g. as follows:

void f( c&& cr ) {
   c cr2(std::move(cr));
}
1
On

You didn't move any object.

std::move is actually quite confusing, because it does not move anything. only a move-constructor or a move assignment operator can move objects, what std::move do is just cast an l-value reference (&) to r-value-reference(&&).

the move constructor, or the move assignment operator can bind to the r-value-refernce(&&) and steal the object contents.