Search code examples
c++move-semanticstemporary-objectscopy-elision

How to force the call to move constructor and why should I do that?


I tested this code to see that the compiler automatically transfers temporary object to variables without needing a move constructor.

#include <iostream>

using namespace std;

class A
{
    public:
    A()
    {
        cout<<"Hi from default\n";
    }

    A(A && obj)
    {
        cout<<"Hi from move\n";
    } 
};

A getA()
{
    A obj;
    cout<<"from getA\n";
    return obj;
}

int main()
{
    A b(getA());

   return 0;
} 

This code prints "Hi from default from getA" and not the presumed "Hi from move"

In optimization terms, it's great. But, how to force the call to the move constructor without adding a copy? (if I wanted a specific behavior for my temporary objects)

Complementary question: I though that if I did not write a move constructor there would be a copy each time I would assign a rvalue to a lvalue (like in this code at the line A b(getA());). Since it's not the case and the compiler seems to do well, when is it really useful to implement the move semantics?


Solution

  • In optimisation terms, it's great. But, how to force the call to the move constructor without adding a copy ? (if I wanted a specific behavior for my temporary objects)

    Normally this requires disabling an optimization flag in order to get this behavior. For gcc and clang you can use -fno-elide-constructors to turn off copy elison. For MSVS it will not do it in debug mode(optimizations turned off) but I'm not sure if they have a specific flag for this

    You can also call std::move in the return statement which will disable the elision and force the compiler to generate the temporary and move from it.

    return std::move(obj);
    

    That said, RVO/NRVO is something that you should want. You shouldn't want to even have the temporary created if you can help it as less work done means more work you can do in the same time. To that end C++17 introduces guaranteed copy elision which stops those temporaries from even existing.

    This doesn't mean you should not write a move constructor if you can (well if you follow the rule of zero then you would not write one and just use the compiler provided one). There may be times where the compiler cannot elide a temporary or you want to move an lvalue so it is still a useful thing to have.