Move in C++ without a std:move

hackernews

In one of my earlier posts, Why you should use std::move only rarely I said that you should use std::move only rarely. In today's post, I would like to show you the benefits of this advice: best performance by default.

Return value optimization

One of the worst enemies for performance are unnecessary copy operations.

You surely heard of return value optimization (RVO). This is what you should always aim for if possible. RVO implies that the returned object isn't created on the stack inside the function but at the call-side, where the value will end up anyway. This spares you copy and move. The standard referees to this a copy elision, as the standard never talks about compiler optimizations.

You get guaranteed copy elision since C++17 in the following case:

1
2
3
4
AppleRVO()
{
return{};
}

This is pure RVO. Then you have named return value optimization (NRVO):

1
2
3
4
5
6
AppleNRVO()
{
Appleres{};

returnres;
}

The latter is not subject to guaranteed copy elision. You probably don't pay for a copy or move there as well.

Move instead of copy

The next best thing after copy elision is moving an object. The language had a few places where implicit moves happened since C++11:

1
2
3
4
AppleFun(Appleval)
{
returnval;
}

In this code, the resulting object is moved from the parameter.

Do you like this content?

I'm available for in-house C++ training classes worldwide, on-site or remote. Here is a sample list of my classes:
  • From C to C++
  • Programming with C++11 to C++17
  • Programming with C++20
All classes can be customized to your team's needs. Training services

But we had cases in the language where things have been a bit more complicated.

The first one I'd like to share is the following:

1
2
3
4
AppleCat(Apple&&val)
{
returnval;
}

You have a function that takes a rvalue reference parameter and returns the just received object. While this code compiles, you will get a copy construction of the return value before C++20. Well, with a conforming compiler like GCC. The less-conforming compiler Clang gives you a move construction. Yes, sometimes not playing by the book can be better.

The other example that I disliked more is the following:

1
2
3
4
AppleCat(Apple&&val)
{
returnval;
}

You once again have a function taking an rvalue reference parameter, but this time, a rvalue reference is returned. This time the code would not compile without moving the return value manually. Which goes against my advice.

To be fair, to get the best result in both cases, moving the return value is required. So both cases go against my advice.

Once you switch your compiler to C++23 mode, both cases do an implicit move. No std::move required. You now can follow my rule to use std::move rarely even more!

Andreas

Source: hackernews

arrow_back Back to News