3

I just stumbled upon a code like this:

std::vector<std::unique_ptr<Fruit>> fruits;
fruits.emplace_back(new Fruit);

(from here)

So in the code we have a raw pointer (new produces a raw pointer, right?) pushed into a vector of unique pointers. And the code works! But why?

Because this does not compile:

std::unique_ptr<Fruit> f = new Fruit();

Is it a bit of under-the-hood magic, for convenience?

Also, what are the possible pitfalls of this approach instead of explicit fruits.emplace_back(std::make_unique<Fruit>())? I've read that make_unique is the preferred method of creating unique pointers.

Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89

2 Answers2

5

And the code works! But why?

vector::emplace_back takes a list of arguments, and just forwards them to the constructor of the element that it creates.

unique_ptr<Fruit>::unique_ptr(Fruit*) is a constructor that takes ownership of the passed bare pointer.

Also, what are the possible pitfalls of this approach instead of [make_unique]

The big pitfall is that emplace_back may throw an exception, in which case no unique_ptr has been successfully created. In such case, the allocated object passed to emplace_back() would be leaked. Therefore, passing a unique_ptr rather than a bare pointer is preferable so the raw pointer is always owned by someone.

Minor point regarding the use of new instead of std::make_unique() in general: It leaves the program "imbalanced" in a way that prevents you from reasoning about the correctness of the program using the age old rule of thumb "delete exactly as many pointers as you new". In this case, you have a correct program with more new than delete. Whether this is an issue is matter of personal opinion.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thank you. Do you know if `make_unique` should still be preferred? – Nurbol Alpysbayev Sep 06 '19 at 22:25
  • 2
    This use of `new` is correct except for the corner case of `vector.emplace_back` throwing due to lack of memory. In that case, the `new Fruit` leaks. – Justin Sep 06 '19 at 22:27
  • @Justin oh, right. That's very important. Added to the answer. – eerorika Sep 06 '19 at 22:27
  • "*In this case, you have a correct program with more `new` than `delete`*" - but you don't, really. The `delete` is merely hidden inside the `unique_ptr` that `std::make_pointer()` returns, but it still exists nonetheless. – Remy Lebeau Sep 06 '19 at 23:29
  • @RemyLebeau `The delete is merely hidden inside the unique_ptr` That's exactly the point. It is not in **your** code. It is hidden within somebody elses code. – eerorika Sep 06 '19 at 23:30
  • @eerorika Not any different than any other kind of smart pointer (`bstr_t`, `_com_ptr_t`, etc) that takes a pre-allocated memory as input and releases it for you. – Remy Lebeau Sep 06 '19 at 23:33
  • @RemyLebeau Indeed. They also share the same problems unless paired with an analogous `make_X` function. – eerorika Sep 06 '19 at 23:34
3
std::unique_ptr<Fruit> f = new Fruit();

doesn't work but

std::unique_ptr<Fruit> f{new Fruit()};

works. The first one does not work because, syntactically, it uses copy initialization. Copy intialization does not work for std::unique_ptr since the constructor that accepts a pointer as argument is explicit.

fruits.emplace_back(new Fruit);

works because of the presence of the constructor used above.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Thanks. Do you know if I `make_unique` should still be preferred over this shorted version? – Nurbol Alpysbayev Sep 06 '19 at 22:25
  • 1
    @NurbolAlpysbayev, I would personally prefer to use `make_unique` instead of `new Fruit`. It removes `new`/`delete` imbalance in your own code. Other people may prefer use of `new Fruit` for reasons that I haven't thought of. – R Sahu Sep 06 '19 at 22:34
  • Thank you! I've already decided to choose `make_unique`, you have strengthen my decision. – Nurbol Alpysbayev Sep 06 '19 at 22:35
  • 1
    The assignment is not working because the constructor that takes a pointer is marked explicit, not because there is no copy constructor. – Timo Sep 06 '19 at 23:00