5

Are there any advantages of using std::unique_ptr<T>& instead of std::unique_ptr<T>? For example, in function arguments?

InFamous X
  • 399
  • 1
  • 4
  • 12
  • The latter won't work as a function argument unless move-semantics are viable. A similar, but more encompassing question, [can be seen here](https://stackoverflow.com/questions/17368644/passing-smart-pointers-by-reference). – WhozCraig Jul 18 '18 at 09:52

6 Answers6

10

There are a few different situations

  1. You want to transfer ownership to the function
    • Use std::unique_ptr<T>
  2. You want to allow the function to modify the pointer
    • Use std::unique_ptr<T> &
  3. You want to allow the function to modify the pointee
    • Use T &, and dereference at the call site
    • If the pointer may be null, instead use T * and call unique_ptr::get at the call site
  4. You want to allow the function to observe the pointee
    • Use const T &, and dereference at the call site
    • If the pointer may be null, instead use const T * and call unique_ptr::get at the call site
  5. You want to allow the function to have a copy of the pointee
    • Use T, and dereference at the call site
  6. You have a ranged-for loop (or <algorithm> call) over a collection of unique_ptr<T>, and want to observe the values, and don't have ranges::indirect (or similar)
    • Use auto &, and know that it is inferred as std::unique_ptr<T> & (or const std::unique_ptr<T> & if the collection is const), so you have to dereference in the loop body
Caleth
  • 52,200
  • 2
  • 44
  • 75
4

Since std::unique_ptr can't be copied by design, a function taking std::unique_ptr<T> can only be invoked by passing ownership of the pointer to the function (i.e. move construction). The calling code will no longer be able to access the pointer.

Using std::unique_ptr<T>&, you are allowing the function to 'see' the pointer and access its member, but the calling code retains ownership, so will still be able to use it itself.

user31601
  • 2,482
  • 1
  • 12
  • 22
  • 6
    Note that if a function just uses the pointed-to object and does not manage its ownership, then it makes no sense to pass unique pointer by reference. Ordinary pointer (or a reference to object type or its base) is much better there. – Daniel Langr Jul 18 '18 at 09:57
1

std::unique_ptr can only be moved so if you pass unique_ptr by value then you cannot extract its contents after function call but if you pass by reference then value can be retrieved. Below is sample code for the same :

#include <iostream>
#include <memory>
void changeUniquePtrReference(std::unique_ptr<int>& upr)
{
    *upr = 9;   
}
void changeUniquePtrValue(std::unique_ptr<int> upv)
{
    *upv = 10;
}
int main()
{
  std::unique_ptr<int> p(new int);
  *p =8;
  std::cout<<"value of p is "<<*p<<std::endl;
  changeUniquePtrReference(p);
  std::cout<<"value of p is "<<*p<<std::endl;
  changeUniquePtrValue(std::move(p));
  std::cout<<"Memory deallocated so below line will crash.. "<<std::endl;
  std::cout<<"value of p is "<<*p<<std::endl;

  return 0;
}
PapaDiHatti
  • 1,841
  • 19
  • 26
1

The two variants are different and apply in specific situations.

If your function takes a std::unique_ptr<T>, this means you take ownership over the managed object and will delete it or save it somewhere else.

If you take std::unique_ptr<T> &, you do not take ownership, but you may change what the pointer points to, e.g. by resetting it to a different object.

If you do not plan to change the pointer itself, you would just pass a T&, or a const T&, depending on whether you plan to modify the object. It is not useful to pass a const std::unique_ptr<T> & since that does not enable you to do more than with the T&, with the disadvantages of a potential extra indirection and an unnecessary restriction on the argument.

PaulR
  • 3,587
  • 14
  • 24
0

Herb Sutter explains all cons and pros of passing smart pointers to functions with all possible variations. Please read the article for more info, its really good:

Brief, it does not make sense passing the unique_ptr to function without reference, unless if your intended behavior is to relinquish ownership of the pointer.!

Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • 4
    It absolutely does make sense to pass the `unique_ptr` — if your intended behaviour is to relinquish ownership of the pointer. – pdpi Jul 18 '18 at 09:55
  • 1
    sorry, my bad @pdpi, edtted the answer – Eduard Rostomyan Jul 18 '18 at 09:57
  • 2
    Further, unless the intent is to modify the `std::unqiue_ptr` *itself* (release or reset what it points to), passing it by reference makes little sense; you'd be just as well to make the argument an `Object&` and pass `*ptr` from the caller side if you intend on the caller keeping ownership of the smart pointer gut. – WhozCraig Jul 18 '18 at 09:57
0

Personally, I see only one case where you should pass a reference to a unique_ptr: That is when the method you are calling might internally decide to delete the object, or relinquish it. This should be very rare, as the owner is giving up the decision on ownership, and honestly it is not great design.

If the child object always takes away ownership or deletes the object then the argument type should be a unique_ptr value and pass ownership immediately.

If the child object simply wants to access the pointer content then the argument should be a raw pointer. You can make an argument for a const unique_ptr reference which will stop you from releasing it, but what is the point? You can decorate that raw pointer type name so you know you don't own it, if you feel you need a convention beyond raw.

Gem Taylor
  • 5,381
  • 1
  • 9
  • 27