As expressed in the title, I was wondering if there is a valid reason/example for which a given class method, excluding move constructor and move assignment operator, or free-function should take, as input parameter, a R-value reference.
-
Why would you exclude free functions but not member functions? What's the difference? – Christian Hackl Dec 02 '16 at 17:46
-
What about classes intended to manage resources? It makes sense for them to be able to take an instance of their managed resource by rvalue reference, so you can directly pass them the result of an operation that returns that resource without needing a middle-man. – Justin Time - Reinstate Monica Dec 02 '16 at 19:44
-
1@ChristianHackl I did not exclude free-functions, I just excluded move constructor and move assignment operator in that they are "trivial" examples. – FdeF Dec 03 '16 at 15:25
-
1@FdeF: Ah yes, I just re-parsed your sentence and I get it now :) – Christian Hackl Dec 03 '16 at 15:26
4 Answers
Avoiding unnecessary copies when retaining values. I like to refer to this functions as "sink functions". This occurs very often when defining setters.
class A
{
private:
std::string _s;
public:
void setS(const std::string& s) { _s = s; }
void setS(std::string&& s) { _s = std::move(s); }
};
int main()
{
A a;
std::string s{"some long string ......."};
a.setS(s); // copies and retains `s`
a.setS(std::move(s)); // moves and retains `s`
}

- 90,666
- 33
- 258
- 416
-
I think rather than compare `std::string && s` with `const std::string &`, it's more interesting to compare it with just taking `std::string s` by value. That's usually the alternative people have in mind to `std::string &&`. – Chris Beck Dec 02 '16 at 17:44
-
See also: http://stackoverflow.com/questions/37935393/pass-by-value-vs-pass-by-rvalue-reference – Chris Beck Dec 02 '16 at 17:50
-
3@ChrisBeck: yes, but it's not as efficient as having two overloads. There is a lot of discussion on this online: http://blogs.microsoft.co.il/sasha/2014/08/21/c-sink-parameter-passing/ – Vittorio Romeo Dec 02 '16 at 17:52
-
I have a question related to this. The same code compoles if the overloaded Method with the rvalue reference internally does not do any move, but a copy instead. So the question is: the Method signature does not garantees that a move is performed, therefore one cannot assume nothing on the valodoty of the moved string after the call to the setter Method. Is this correct? – fedino Dec 02 '16 at 18:25
-
@fedino the caller should not assume anything about the state of the moved string afterwards, that is correct. But I would say that is almost always true of a moved from variable. For example, with the small string optimization a move may be implemented as a copy and I think it would be within the rights of a compiler to leave the moved from string as it was. – Chris Drew Dec 02 '16 at 19:28
-
@VittorioRomeo thanks for the link. I've learned a lot from that :) – Farhad Reza Dec 02 '16 at 20:34
There are two ways to model a sink argument.
The first is by taking by-value:
void foo(std::string);
the second is by rvalue reference:
void foo(std::string&&);
with the possible variant of including a const&
overload to simplify work for the caller.
inline void foo(std::string const& s){
auto tmp = s;
return foo(std::move(tmp));
}
The take-sink-by-value has an extra overhead of a single std::move
over taking it by &&
and const&
(or requiring caller to manually copy a non-temporary value and move it in themselves). It doesn't require the 2nd overload.
So if that one move is worth accounting for, taking sink arguments by const&
and &&
can save you a move. Plus, if copy is extra expensive, you can make it awkward at the call site and thus discourage it.
But that isn't the only reason. Sometimes you want to detect if something is an rvalue or lvalue, and only copy if it is an rvalue.
As an example, suppose we had a range adapter backwards
. backwards
takes an appropriate range (something you can for(:)
over, and whose iterators can be reversed) and returns a range that iterates over it backwards.
Naively, all you have to do is get begin
and end
from your source range, then make reverse iterators and store them and return them from your own begin
and end
methods.
Sadly, this breaks:
std::vector<int> get_some_ints();
for( int x : backwards( get_some_ints() ) ) {
std::cout << x << "\n";
}
because the lifetime of the temporary returned from get_some_ints
is not extended by the for(:)
loop!
That for(:)
expands to roughly:
{
auto&& __range_expression = backwards( get_some_ints() );
auto __it = std::begin( __range_expression );
auto __end = std::end( __range_expression );
for (; __it != __end; ++__it) {
int x = *__it;
std::cout << x << "\n";
}
}
(There are some small lies told to children above, but it is close enough for this discussion).
In particular this line:
auto&& __range_expression = backwards( get_some_ints() );
the return value of backwards
is lifetime extended; but the lifetime of its arguments are not!
So if backwards
takes a R const&
, the vector
is silently destroyed prior to the loop, and the iterators involved are invalid.
So backwards
must store a copy of the vector
for the above code to be valid. That is our only opportunity to make the vector last long enough!
On the other hand, in a more conventional case:
auto some_ints = get_some_ints();
for( int x : backwards( some_ints ) ) {
std::cout << x << "\n";
}
storing an extra copy of some_ints
would be a horrid idea and quite unexpected.
So in this case, backwards
needs to detect if its argument is an rvalue or an lvalue, and if it is an rvalue it needs to copy it and store it in the return value, and if it is an lvalue it needs to either just store iterators or a reference to it.

- 262,606
- 27
- 330
- 524
Sometimes you want to take ownership of something large like a std::vector
but you want to avoid making a copy by accident.
By only providing an r-value reference overload a caller who wants to pass a copy has to do so explicitly:
class DataHolder {
std::vector<double> a;
std::vector<int> b;
public:
DataHolder(std::vector<double>&& a, std::vector<int>&& b) : a(a), b(b) {}
};
auto a1 = makeLotsDoubles();
auto b1 = makeLotsInts();
DataHolder holder(std::move(a1), std::move(b1)); // No copies. Good.
auto a2 = makeLotsDoubles();
auto b2 = makeLotsInts();
DataHolder holder(a2, b2) // Forgot to move, compiler error.
If instead, you had used pass-by-value then if you forget to use std::move
on an lvalue then a copy is made.

- 14,926
- 3
- 34
- 54
It's useful to transfer an uncopyable object to the class. Some objects have no copy constructor, which means they can't be passed by value. One sees this with RAII, where creating a copy would acquire a new set of resources. Or in general, when the copy constructor and/or destructor have side effects outside the object being (de)constructed. Making a copy might not be merely inefficient, like copying a std::string, but quite impossible.
One way to pass an object like this is to pass it as a pointer. For example:
// Creates a file on construction, deletes on destruction
class File;
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(logfile1); // OK
logger.add_output(&logfile2); // BAD!
}
File
has no copy constructor, as a copy would create and delete the same file as the original, which makes no sense. So we pass a pointer to the file to the logger
object to avoid copying. With logfile1
it works ok, assuming of course that logger
deletes the File when it's done with it. But logfile2
has two big problems. One is that logger
can't delete it since it wasn't allocated with new
. The other is that logfile2
will be destructed, deleting the file and invalidating the pointer to it saved in logger
, when logfile2
leaves scope at the end of the block.
We could give File
a move constructor, which would transfer ownership of the file to the destination File and make the source File "empty". Now we can write the above code in a way that works.
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(std::move(*logfile1));
logger.add_output(std::move(logfile2));
logger.add_output(File("name3"));
delete logfile1;
}
We can now create a File
with new, or as a local object, or even as an unnamed pr-value. The use of std::move
also makes it clear, at the call site, that ownership of the File
is transferred to logger
. With the pointer ownership transfer isn't clearly shown, one depends on logger.add_output()
documenting the semantics and checking that documentation.

- 4,240
- 24
- 35
-
An object with no copy constructor can be passed by value if it has a move constructor: http://melpon.org/wandbox/permlink/YYmU43P1ZLKlLJKx – Chris Drew Dec 02 '16 at 20:42