0

Suppose I have the following class:

class foo {  
    std::unique_ptr<blah> ptr;
}

What's the difference between these two:

foo::foo(unique_ptr p) 
   : ptr(std::move(p))
{ }

and

foo::foo(unique_ptr&& p)
   : ptr(std::move(p)
{ }

When called like

auto p = make_unique<blah>();
foo f(std::move(p));

Both compile, and I guess both must use unique_ptr's move constructors? I guess the first one it'll get moved twice, but the second one it'll only get moved once?

Praetorian
  • 100,267
  • 15
  • 224
  • 307
atanamir
  • 4,483
  • 3
  • 21
  • 18
  • std::move is a simple cast to rvalue, it doesn't really do anything in behind – Ivan Sanz-Carasa Feb 14 '18 at 09:30
  • Does this answer your question? https://stackoverflow.com/q/8114276/241631 You're right that the second one has 2 moves, one for constructing the parameter and then for constructing the data member. – Praetorian Feb 14 '18 at 14:22

2 Answers2

0

They do the same, which is moving the pointer twice (which translates to 2 casts). The only practical difference is the place where the code would break if you were to pass a std::unique_ptr by value.

Having foo::foo(unique_ptr p) the compiler would complain about the copy constructor being deleted.

Having foo::foo(unique_ptr&& p) it would say that there is no matching function for the set of arguments provided.

amc176
  • 1,474
  • 6
  • 18
-1

std::unique_ptr is a non-copyable class, this means that you either pass a constant reference (readonly access) or move it (rvalue) to express/give ownership.

The second case (getting explicit rvalue) is the proper implementation, as your intent is passing the ownership and std::unique_ptr has deleted copy assignment/constructor.

In the first case, copy is prohibited and the arg is an rvalue (you moved it before passing) so the compiler uses the move constructor. Then the ownership belongs to the new argument and you move it again to the internal field passing the ownership again, not a very nice solution. (Note that this declaration isn't restrictive, it allows any value type. If std::unique_ptr would be copyable, copy contructor would be used here, hiding the actual problem).

In terms of resulting code, std::move is a simple cast to rvalue, so both do the same. In terms of correctness, second one must be used.

Ivan Sanz-Carasa
  • 780
  • 4
  • 12
  • 1
    While there has been some disagreement about this in the C++ community, there are some really knowledgable people (like Herb Sutter) that advocate passing unique_ptr by value to indicate a 'sink' parameter. It clearly shows the intent of the programmer (sink), as it makes sure that once passed as such, the pointer is guaranteed to be moved from. One can argue that the rvalue reference solution is more efficient, because it involves 1 constructor call less (the one needed to construct the parameter), but saying "second one must be used" is, in my opinion, unjustified. Hence the downvote. – mdx May 01 '20 at 10:08