Ákos Vandra-Meyer
Ákos Vandra-Meyer

Reputation: 2166

C++11 Move constructor optimization

I'm currently trying to get a hang of move constructor. I came upon the following (Compiled using g++ d.cpp --std=c++11 -O3)

class A {
    string _x;
public:
  A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
  A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x;  }
  A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x;  }

  A foo() {
    cout << "foo: " << _x << "\n";
    return A("foo");
  }
};

int main()
{
  A a;
  A b = a;
  b.foo();
}

I expect this to output:

default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo

However the output is:

default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo

Why isn't the A b = a line optimized to use the move constructor? The a object is never used afterwards, so it would be safe to optimize the code to use it instead of the copy constructor.

I know I could force the move contructor to be invoked with std::move(), but I'd prefer this to happen automatically in cases like this one.

Upvotes: 2

Views: 463

Answers (3)

Danqi Wang
Danqi Wang

Reputation: 1637

Why isn't the A b = a line optimized to use the move constructor?

What you can do in copy constructor and move constructor could be totally different. The compiler cannot guarantee that the results of the two constructors are identical. Implementing this kind of optimization has the potential of changing the behavior of your program, which breaks the as-if rule.

You need to use std::move to cast a to A&&:

#include <utility>
int main()
{
  A a("a");
  A b = std::move(a);
  b.foo();
}

A correct implementation of the move constructor should be:

A(A&& other)
: _x(std::move(other._x))
{}

After the line A b = std::move(a);, a should be "empty". In this case, a._x will be empty. as pointed by @TonyD in the comments, a._str could be in an unspecified but valid state (move constructor of std:string). You should use a with caution after this line.

Upvotes: 7

Danvil
Danvil

Reputation: 22981

A b = a; always invokes the copy constructor, no matter if it could invoke the move constructor. Additionally the lifetime of the object a continues after the assignment, even it is not used anymore.

If you want to use the move constructor, you have to make it explicit:

A b = std::move(a);

Note that this can be dangerous, as a is still accessible after the move. If you accidentally use it later, there may be undefined behavior.

Think about why it should happen automatically. In the example you gave, there is no need, as you can as well use a instead of b. In many cases where it would make more sense move constructor/assignment would be used automatically, e.g. A a; a = foo();.

Upvotes: 5

Benjamin Lindley
Benjamin Lindley

Reputation: 103693

Why isn't the A b = a line optimized to use the move constructor?

Because that would change the observable behavior of the program. The compiler is not permitted to freely change the observable behavior of the program (§1.9/1), except under very specific circumstances (§12.8/31). This is not one of those circumstances. Remove the side effects from your constructors, and the compiler may optimize them away. Of course, if you remove the side effects, then you won't notice if the compiler optimizes the constructor calls away (unless you examine the assembly or binary output), but that's the whole point.

Upvotes: 4

Related Questions