Home > C++11 > Time To Get Moving!

Time To Get Moving!

Prior to C++11, for every user-defined type we wrote in C++03, all standards-conforming C++ compilers gave us:

  • a “free” copy constructor
  • a “free” copy assignment operator
  • a “free” default constructor
  • a “free” destructor

The caveat is that we only got them for free if we didn’t manually override the compiler and write them ourselves. And unless we defined reference or pointer members inside of our type, we didn’t have to manually write them.

Starting from C++11 on, we not only get those operations for free for our user-defined types, we also get these turbo-boosters:

  • a “free” move constructor
  • a “free” move assignment operator

In addition, all of the C++ standard library containers have been “move enabled“.

When I first learned how move semantics worked and why this new core language feature dramatically improved program performance over copying, I started wondering about user-defined types that wrapped move-enabled, standard library types. For example,  check out this simple user-defined Msg structure that encapsulates a move-enabled std::vector.

MsgStruct

Logic would dictate that since I get “move” operations from the compiler for free with the Msg type as written, if I manually “moved” a Msg object in some application code, the compiler would “move” the vDoubs member under the covers along with it – for free.

Until now, I didn’t test out that deduction because I heard my bruh Herb Sutter say in a video talk that deep moves came for free with user-defined types as long as each class member in the hierarchical composition is also move-enabled. However, in a more recent video, I saw an excellent C++ teacher explicitly write a move constructor for a class similar to the Msg struct above:

ManualMoveCtor

D’oh! So now I was confused – and determined to figure out was was going on. Here is the program that I wrote to not only verify that manually written “move” operations are not required for the Msg struct, but to also measure the performance difference between moving and copying:

MoveCopyPerf

First, the program built cleanly as expected because the compiler provided the free “move” operations for the Msg struct. Second, the following, 5-run, output results proved that the compiler did indeed perform the deep, under the covers, “move” that my man Herb promised it would do. If the deep move wasn’t executed, there would have been no noticeable difference in performance between the move and copy operations.

copymoveperf

From the eye-popping performance difference shown in the results, we should conclude that it’s time to start replacing copy operations in our code with “move” operations wherever it makes sense. The only thing to watch out for when moving objects from one place to another is that in the scope of the code that performs the move, the internal state of the moved-from object is not needed or used by the code following the move. The following code snippet, which prints out 0, highlights this behavior.

postmovestate

  1. Cesar Mello
    June 18, 2015 at 9:19 am

    Your blog is very nice, congratulations!! Best regards!

  2. Gyula Chinoradszki
    June 26, 2015 at 5:51 am

    I tried the sample code with VS2013 but it did not work for me. I mean both operations took the same amount of time.

    Only after I implemented the move ctor and the move assignment for the UDT by hand, that I finally saw the difference.

    • June 27, 2015 at 4:19 am

      Interesting. I duplicated your results exactly. I printed out the size of the moved-from object msg3.vDoubs after the std::move(msg3) and it showed that even though std::move() was used, the move did not occur.

      Since the first version of the program compiled successfully, and we used std::move() in the code, the move ops must have been supplied by the compiler for free(?). However, as we’ve both seen, the compiler chose not to execute the move – it copied instead. I can only conclude that either the standard doesn’t require the compiler to perform the move in that case, or VS2013 is not compliant in that area. Weird.

      BTW, I used GCC 4.9.2. for my testing.

      • June 30, 2015 at 11:53 am

        MSVC is indeed non-compliant in this regard. I believe this has been addressed in MSVC 2015 but I haven’t given it a proper going over yet.

        Worse, if you’re writing code targeting multiple compilers, compliant compilers will take a user-defined move constructor as a cue to suppress auto-generation of a copy constructor. If one of your targets is MSVC this leads to very messy class definitions.

      • June 30, 2015 at 11:57 am

        Ah, that will teach me not to read ahead… Your subsequent post covers this.

        Cheers,
        Guy

      • June 30, 2015 at 12:49 pm

        🙂

  1. June 21, 2015 at 1:01 am

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.