5

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.

5
  • The first one passes by value and therefore won't compile with unique_ptr since it is non-copyable. Commented Aug 19, 2014 at 8:32
  • 2
    @JonathanPotter - you are wrong, it can compile if you use move() Commented Aug 19, 2014 at 8:35
  • A Brief Introduction to Rvalue References. Commented Aug 19, 2014 at 8:35
  • 2
    f does take ownership in the first case, whereas f may take ownership for the second case. Commented Aug 19, 2014 at 8:41
  • 1
    You don't even have to use move, it just has to be an r-value, e.g. a function return value, or a direct call to the constructor. Commented Aug 19, 2014 at 8:43

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 for the final word (if nothing else).
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 -- and forgetting the move can silently degrade performance, despite every intention on part of the programmer to request move semantics.
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<T>' in contrary to the by-rvalue-ref version no matching function for call to 'f'. IMO the first one is much clearer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.