Boost logo

Boost :

Subject: Re: [boost] rvalue ref best practices?
From: Simonson, Lucanus J (lucanus.j.simonson_at_[hidden])
Date: 2012-06-12 13:51:44


Dave Abrahams wrote:
>> If you aren't in a loop you probably don't care about performance. I
>> know I intend to stick with pass by reference. It is very useful to
>> clearly understand and be able to easily reason about the ownership of
>> resources in a program.

>If that's your primary concern, why on earth wouldn't you use pass-by-value?

Let's take a sort of extreme example:

A a;
mutate(a, b); //a passed by reference as in/out
mutate(a, c);

vs.

A a;
a = mutate_rvr(std::move(a), b);
a = mutate_rvr(std::move(a), c);

Here I compare an accumulate semantic with and without move semantics. Both make zero copies. The latter results in four moves of a while the former has zero moves of a. The former is less readable because you need to know that the first argument is the output. In my coding convention such functions are always named with a verb describing the action taken on the in/out parameter and the in/out is the first argument. A function that returns by value is named with a noun describing the return value. By convention I always know what is going on when I pass by reference for in/out parameters. The second allows equational reasoning, but composes the same since mutate returns a reference to A, again by my personal convention.

mutate(mutate(a,b),c);

a = mutate_rvr(mutate_rvr(std::move(a), b),c);

I just don't find it easier to think about an interface that requires that I transfer ownership of an object into it so that it can modify the object then return it by value to transfer ownership back. The reason I don't find it easier is because it is unnecessary and error prone. It is very easy to forget to call move.

A a;
a = mutate_rvr(a, b);
a = mutate_rvr(a, c);

It will compile, function correctly passing tests, but it is now a performance bug. To catch these bugs I need to put a logger in the copy constructor/assignment operator and run a special test to audit move/copy behavior. (or make A a move only type, which seems draconian)

I also find copy Ellison and RVO harder to reason about than reference semantics because it is more complex and less explicit in the code to figure out whether the optimizations will be applied.

I would rather use std::move in the special cases where I no longer should have ownership of an object in the current scope because the design intent is to transfer that ownership somewhere else (into a container, for example). These are the places I am returning/passing by value in C++03. I don't know where to draw the line in changing coding style to make use of move semantics, but I don't think it should be drawn at either extreme, and I see very little downside to drawing it closer to the C++03 style that already works very well for me. There is no doubt that I would use move semantics everywhere that transfer of ownership is necessary and the performance advantage of move versus copy is beneficial. Everywhere else I'm inclined to continue to use references or make copies. We can do three things. We can copy, we can refer-to and we can transfer-ownership-of. There is plenty of occasion to need to do all three when programming. I would think that we should all just do whichever best fits our design intent in any given situation rather than try to prefer one over another.

Regards,
Luke


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk