0

The typical reasoning behind the introduction of rvalue references into C++ is to eliminate (optimize) superfluous copying during the evaluation of complex C++ expressions.

However, there are two compiler optimization techniques for C++98/C++03, which basically serve the same purpose, namely

Is there any real-world use case, when the above techniques (with appropriately written code) fail to eliminate superfluous copying, but rvalue references can succeed?

  • 6
    "The typical reasoning behind the introduction of rvalue references into C++ is to eliminate (optimize) superfluous copying during the evaluation of complex C++ expressions." -- No, the typical reasoning is about move semantics. "Is there any real-world use case, when the above techniques (with appropriately written code) fail to eliminate superfluous copying, but rvalue references can succeed?" -- rvalue references can be completely replaced by coding in inline asm. – Jim Balter Aug 13 '13 at 07:58
  • Yeah, we can't live w/o those rvalues refs -- we need them to 0ptimize our cool stuff!! – mlvljr Aug 13 '13 at 08:50

3 Answers3

16

If you need one single argument why rvalue references are essential, it's unique_ptr. It's the archetype of resource managing SBRM classes. Resources are by definition not copyable, but the managers should be movable

An owning container of heterogeneous, related objects is straight-forward to implement as a container of unique_ptrs of the base class, and it is a very common programming idiom. Before rvalue references, it was never quite possible to implement this cleanly in "native C++", and would always require manual housekeeping.

Threads and locks are further examples of resources.

In a nutshell, optimization is just one part of the story, but movability is a quality in its own right that just wasn't very easy to address before rvalue references.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • +1 r-value references are before all about **semantics**. The new-found ability to transfer unique types and having those transfer (more or less) checked by the compiler is a real boon. – Matthieu M. Aug 13 '13 at 08:07
  • 1
    @MatthieuM.: Yeah... move-only value semantics is what you want. For this you need a copy constructor that can modify its argument (this was already possible in C++98), but that can also bind to temporary values (this is new). – Kerrek SB Aug 13 '13 at 08:23
  • Interesting. The point about threads and locks is a very good example; I'd always considered it for more complex objects, like a log stream, where the destructor has significant behavior, and you don't want to support true copy, but you do want to be able to return it from a function. But grabbing a lock in a function, and returning it with the object, is another good use. – James Kanze Aug 13 '13 at 08:38
  • @KerrekSB: It was arguably possible, however `auto_ptr` was universally despised because it did. The point of move semantics is that: you have *explicit* requirements for asking for a move if the source is not a temporary and the Standard specified what the state of the source was afterward (well, it marked it as unspecified and only to be destroyed/assigned to). – Matthieu M. Aug 13 '13 at 08:58
5

rvalue references are designed with two aims in mind:

  • move semantics
  • perfect forwarding

Move semantics

One purpose of move semantics, in general, is an optimization of copy. Where you can copy, you can just move the resources held by the object.

std::string str("The quick brown fox jumps over the lazy dog.");
std::cout << str;      // Do something
func(std::move(str));  // You do not need str in the subsequent lines, and so move

Also, the compiler can optimize the return value of a function, where RVO may not be applicable

std::vector<std::string> read_lines_from_file(const std::string& filename);

auto lines = read_lines_from_file("file1.txt");  // Can possibly be optimized with RVO
// Do something with lines
lines = read_lines_from_file("file2.txt");  // RVO isn't applicable, but move is
// Do something with lines

If you implement move semantics in your classes, chances are you will experience a significant performance boost when you are putting them in containers. Consider std::vector. Whenever you modify the elements of an std::vector, at some time, some reallocations and element shifting would happen. In C++98, elements are mostly copied around[1], which is unnecessary, as there is logically only one representation of the object that is needed at a single point of time. With move semantics, containers like std::vector can just move the objects around, eliminating extra copies and therefore boost the performance of your program.

One very important use of move semantics is not just for optimization but for implementing unique ownership semantics. One very good example is std::unique_ptr in which it can only be moved around (it cannot be copied), therefore you are guaranteed (with proper use) that there is only one std::unique_ptr that is managing the contained pointer or resource.


Perfect forwarding

Perfect forwarding uses rvalue references and special template deduction rules for us to be able to implement perfectly forwarding functions, that is, functions that can forward arguments correctly and without making any copies.

Examples are std::make_shared and the upcoming std::make_unique. Also are the new emplace* methods of containers, one that you will find very useful and convenient (not mentioning that it would also be very efficient).


This answer provides a very good and thorough explanation of rvalue references with respect to move semantics and perfect forwarding.


[1] One thing I heard from one of Bjarne Stroustrup's talk[source needed] is that containers typically implement some ad-hoc move semantics. Therefore, as he has said, you may not experience that much of a speed-up.

Community
  • 1
  • 1
Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
  • These are more or less exceptional cases, which an application programmer won't need to consider until the profiler says so. – James Kanze Aug 13 '13 at 08:35
1

There are two reasons for using rvalue references and move semantics.

  • For most programmers, the most important (or even the only reason which affects their code) is the possibility of supporting "limited copy". (I'm sorry, I don't know a good name for this.) Like the iostream classes, for example: you don't want to support copy, per se, but you do want to allow construction in a separate function. A typical example might be a log stream: you want to return the appropriate log stream from a function, but you only want a single instance, whose destructor will ensure that the logging actions (i.e. sending email, posting to syslog, etc.) are atomic.

  • At times, it can also be used for optimization; you mention things like return value optimization and copy elision, but they don't always apply, especially with assignment. Generally, in such cases, you would ignore rvalue references and move until the profiler said there was a problem, but if you're implementing a library, you might not have this luxury.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • "limited copy"? The term you are looking for is "move" ;) – fredoverflow Aug 16 '13 at 20:41
  • @FredOverflow The question was why you would want to use move semantics. To answer "you use move semantics because you want move semantics" seems a bit of a tautology. I'm not happy with the expression "limited copy", but I can't find a better one to express what I mean here. – James Kanze Aug 17 '13 at 22:02