8

It seems an easy way to circumvent a unique_ptr is to use a pointer to the unique_ptr object. And that's not difficult. So using the unique_ptr is sort of a gentleman's agreement and not really super enforced?

#include <iostream>
#include <memory>
using namespace std;

class Box {
  public:
    int num;
};

void update_box(unique_ptr<Box>* pu);

int main(){
  unique_ptr<Box> b{new Box};
  unique_ptr<Box>* u = &b;
  update_box(u);
  cout << b->num << endl; // Outputs 99.
  return 0;
}

void update_box(unique_ptr<Box>* pu) {
  (*pu)->num = 99;
}
Swapnil
  • 1,424
  • 2
  • 19
  • 30
Plasty Grove
  • 2,807
  • 5
  • 31
  • 42
  • 5
    What piece of the contract do you think you are circumventing here? – StoryTeller - Unslander Monica Sep 25 '18 at 11:03
  • Your example shows that you can pass a `unique_ptr` by pointer (or reference) to a function and modify the object that it points to. Why shouldn't you be able to do this? – Mike Borkland Sep 25 '18 at 11:05
  • Having a unique ptr does not mean that you are not allowed to pass it or its pointer to somewhere else, it just says who is the owner of that pointer and responsible for the lifetime of the object it is pointing to. So you could write `void update_box(const unique_ptr &pu);` and you would be able to use `pui->num = 99` in that function. Or you could pass it as raw pointer `u->get()`. – t.niese Sep 25 '18 at 11:06
  • In many cases the smart pointers should not be looked at like a simple self-deleting pointer, but in terms of *ownership*. Sometimes you want a single ownership of a "resource" so you use `std::unique_ptr`. And sometimes you want others to use the resource without passing on the ownership, which can be done by passing a pointer or *reference* to the `std::unique_ptr` object. – Some programmer dude Sep 25 '18 at 11:07
  • 1
    @hellow: Yes it will. A `unique_ptr const&` is not a ``unique_ptr const&`. Entirely unrelated types, in afct. – MSalters Sep 25 '18 at 11:07
  • can you please explain what is wrong about this example? I dont get it ... – 463035818_is_not_an_ai Sep 25 '18 at 11:08
  • @user463035818 - My understanding of `unique_ptr` is that it should be used to ensure you only have one pointer to an object at any time. But by using a pointer to the `unique_ptr` it seems like I now have multiple pointers. – Plasty Grove Sep 25 '18 at 11:12
  • 1
    @PlastyGrove no thats not what a `unique_ptr` is for, at least to my understanding. You can have as many pointers as you like, but only one of them owns the instance and `unique_ptr` helps you to realize that – 463035818_is_not_an_ai Sep 25 '18 at 11:13
  • @StoryTeller - The contract I was expecting was that there's no way to have more than one pointer to the object if I use `unique_ptr`. I guess I haven't used C++ enough yet. – Plasty Grove Sep 25 '18 at 11:14
  • @Plasty_Grove The whole idea is that you only have one pointer that owns the dynamically-allocated `Box` object. As such, when the `unique_ptr` goes out of scope, the `Box` will be safely `delete`d. Having a raw pointer to the `unique_ptr` does not change this fact. – Mike Borkland Sep 25 '18 at 11:14
  • pointers arent really the problem that smart pointers solve, its ownership that they manage, ie who is responsible for deleting the instance – 463035818_is_not_an_ai Sep 25 '18 at 11:15
  • Even if getting a pointer to the `std::unique_ptr` object, you still only have a single pointer to the pointer wrapped by the `std::unique_ptr` object. You can have as many pointers or references to the `std::unique_ptr` object as you want, but there will still be only one `std::unique_ptr` object. – Some programmer dude Sep 25 '18 at 11:16
  • btw I am not aware of any mechanism in C++ to disable taking the adress of something (you can overload `operator*` but you cant overload `addressof`) – 463035818_is_not_an_ai Sep 25 '18 at 11:17
  • 2
    @PlastyGrove unique pointer does not mean that there should only be one pointer to that object. It says something about the ownership. For an `unique_prt` you have only one owner that is responsible and connected to the lifetime of the object. You are allowed to have e.g. one-to-many relations using unique_ptr to the child and raw pointer (or [std::experimental::observer_ptr](https://en.cppreference.com/w/cpp/experimental/observer_ptr)) to the parent. But its your responsibility that the raw (observer pointer) is always valid. – t.niese Sep 25 '18 at 11:18

3 Answers3

18

C++ is, in a sense, full of gentlemen's agreements. In other words, the language gives you the ability to shoot yourself in the foot.

There is nothing to prevent you taking the address of a std::unique_ptr. If you really find that distasteful then you could inherit from std::unique_ptr and overload the address-of operator with a function containing a static assertion.

But even if you did that, you could circumvent that with std::addressof!

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
14

You actually put your real question into a comment:

My understanding of unique_ptr is that it should be used to ensure you only have one pointer to an object at any time.

No, this is wrong. You can easily do a simple:

std::unique_ptr<int> a(new int);
int *b = a.get(); // b points to the same object as a

The design of unique_ptr tries to ensure that you can only have one unique_ptr to an object. But even, it can ensure this only if you don't use naked pointers. With naked pointers, it is easy to sidestep this design:

std::unique_ptr<int> a(new int);
std::unique_ptr<int> b(a.get());

Here, b points to the same object as a, so this program has undefined behavior (because the int object will be deleted twice).

unique_ptr's intent is to show ownership. As there can be only one unique_ptr which can point to an object (ignoring the "hack" I've presented earlier), unique_ptr owns the pointed object, and as unique_ptr's destructor is called, it will delete the pointed object.

geza
  • 28,403
  • 6
  • 61
  • 135
  • Wow - this is insane. Shows I really don't know anything about C++ :|. I didn't realise `unique_ptr` has a `.get()` function. – Plasty Grove Sep 25 '18 at 11:48
  • 2
    @PlastyGrove actually `int* b = new int; std::unique_ptr a(b);` is valid, no `.get` needed. – apple apple Sep 25 '18 at 11:56
  • @appleapple `int* b = new int; std::unique_ptr a(b);` is valid, but is - imho - kind of an antipattern. At least you can get used to something that might become problematic. E.g. if you would start to write something like `int* b1 = new int; int* b2 = new int; std::unique_ptr a1(b1); std::unique_ptr a2(b2);` you might have a problem with exception safety. So a `std::unique_ptr a(new int); int* b = a.get()` is preferable. I would always use the `new` in the initialization of the _smart pointers_ , as then the ownership and intended usage are clear, at its initialization. – t.niese Sep 25 '18 at 13:38
  • @t.niese I'd say then it's better to use [make_unique](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique). I'm just extending this answer (and reply to op's comment). – apple apple Sep 25 '18 at 14:00
  • will `unique_ptr` consider gotten pointer into its lifecycle? – Jesse Dec 12 '22 at 07:28
0

I know that this is answered by multiple people. But what I want to mention is that, there could be real usecase where you need to pass reference of unique_ptr.

Consider the below example :

StatusMsg getMemory(unique_ptr<char[]>& Up) {
   unique_ptr<char[]> mem(new char[100]);
   Up = std::move(mem);
   return StatusMsg::Success;
}

int main() {
   unique_ptr<char[]> Up;
   StatusMsg s = getMemory(Up);
   if (s!=StatusMsg::Success) {
     // throw error
   }
   
   // Do something with allocated memory
}

Here the unique_ptr must be returned (or updated) using call by reference, since the actual return value should be the status msg.

Darshan Bhat
  • 245
  • 1
  • 11
  • See for instance one of my older questions (https://stackoverflow.com/questions/48720204/vector-with-references-to-unique-ptr) where I had a similar use case (a vector with constant references to uniqe_ptr) – pschulz Apr 17 '23 at 07:23