4

What is the difference between the following two declarations and when should I prefer one over the other?

void f(unique_ptr<T> x);
void f(unique_ptr<T> &&x);

It seems to me like both of these mean that f() takes ownership over the pointed object, but I've seen people using them both. I would expect the first to be standard and the second to be considered a bad practice.

tohava
  • 5,344
  • 1
  • 25
  • 47

1 Answers1

7

The second version, the one taking a reference, is a tiny bit more efficient, since there's no separate object constructed for the function parameter. You would find this kind of style in generic library code where you don't want to impose any unnecessary cost on the user.

The first version is a bit easier to read and to remember, and allows the slightly more general advice to "pass things by value which you want to own". This advice doesn't apply quite perfectly in your case, but it does in other cases. Consider a class that owns a string:

struct Foo
{
    std::string s_;
    Foo(std::string s) : s_(std::move(s)) {}
};

By passing the constructor argument by value, you can now construct a Foo from both an lvalue and an rvalue string, and you leave the decision whether to copy or to move to the constructor of std::string, rather than worry about it yourself. This reduces complexity (imagine you had five such arguments).


As a final word of general advice, bear in mind that the C++ standard library considers any value that is bound to an rvalue reference to be unaliased, i.e. referred to only by that reference and nobody else. This is an important "hidden" semantic assumption. So even though an rvalue reference is a reference and you could in principle use it to refer to some shared state, don't go overboard with that, since it would be unexpected and surprising.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • +1 for the final word (if nothing else). – Angew is no longer proud of SO Aug 19 '14 at 08:50
  • One tangential point: "by-value and move" is easy to teach. It's straight-forward and obvious (you have a value, you better move from it). By contrast, many novices get very confused when given an rvalue reference and don't realize that they [still have to move](http://stackoverflow.com/a/13219621/596781) -- and forgetting the move can silently degrade performance, despite every intention on part of the programmer to request move semantics. – Kerrek SB Aug 19 '14 at 09:19
  • 1
    Another thing to note is the kind of error message you get from the compiler if you pass an lvalue (using clang): with the by-value parameter you get `call to implicitly-deleted copy constructor of 'std::unique_ptr'` in contrary to the by-rvalue-ref version `no matching function for call to 'f'`. IMO the first one is much clearer. – klaus triendl Aug 19 '14 at 10:28