4

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?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Nix
  • 351
  • 5
  • 13
  • 4
    `A b(std::move(getA()));` does it on my compiler but note that what's likely happening without `std::move()` is return value optimization (RVO), where the compiler is emitting code that causes `getA()` to construct the returned object `obj` in-place at `b`, eliding the move-constructor call entirely. That is to say, by forcing the move-ctor call you are actually disallowing a compiler optimization. – cdhowie Apr 21 '17 at 12:24
  • 3
    If you're using gcc, try the `-fno-elide-constructor` pessimisation flag. – Mat Apr 21 '17 at 12:26
  • 1
    For your 2nd qu: is you put A in a vector and wanted to add loads more, you might want to move them then on a resize. And move would be called (if you had appropriate no excpets in place) – doctorlove Apr 21 '17 at 12:26
  • 4
    You shouldn't need to force the move. If your code actually relies on the move happening and can't handle it being elided I would almost say that is a bug in the code/design. You should want RVO/NRVO to happen. – NathanOliver Apr 21 '17 at 12:26
  • In other news VS2015 happily calls the move constructor for you ?! – doctorlove Apr 21 '17 at 12:32
  • 1
    @doctorlove Did you compile in release or debug mode? debug mode in 2017 calls the move c'tor but release mode does not which makes sense since debug has no optimizations on. – NathanOliver Apr 21 '17 at 12:34
  • Oh - cool - I learnt something :-) – doctorlove Apr 21 '17 at 12:37
  • To be honest I have to teach the move semantics and I'm looking for a simple exemple to show that they are useful. At this point I'm nearly wanting to say "f*** it, compiler does all very well alone" ! Even after doing a perfect forwarding my move constructor is not called. – Nix Apr 21 '17 at 12:49
  • 1
    @Nix - Most "simple examples" are too simple, and the compiler already knows how to optimize those. C++ compilers are real beasts, doing *lots* of transformations to the code. – Bo Persson Apr 21 '17 at 12:54
  • @Bo Persson I totally agree with you. But if it is so common to read about the move constructors there must be some cases where they are really usefull. And if they are such cases it must be the simpliest case (that can be not simple at all !) – Nix Apr 21 '17 at 13:00
  • You should expect "Hi from default" in all cases as the object must first be constructed at some point... – M.M Apr 21 '17 at 13:10

1 Answers1

4

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.

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402