2

What is the best way to insert a std::unique_ptr made with std::make_unique() into a std::set? Both insert() and emplace() work, but which one is better?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Gordem
  • 115
  • 9
  • It doesn't really matter for types like `std::unique_ptr`. Since you are creating your `std::unique_ptr` before inserting it, both `insert()` and `emplace()` will have to construct a new `std::unique_ptr` for the `std::set` to hold and move your `std::unique_ptr` into it. If your `std::set` held actual objects instead of pointers to objects, then `emplace()`'ing a new object would be "better" than `insert()`'ing a temp object, but with move semantics the differences tend to be negligible. – Remy Lebeau Jul 17 '20 at 17:49
  • I want to construct `unique_ptr` "inline" of insertion. – Gordem Jul 17 '20 at 17:52
  • You can do "inline" constructions with `emplace()`, but that means if you want to avoid constructing and passing in a temp `std::unique_ptr` to move ownership from, you would have to instead pass in a raw pointer to take ownership of. Which means you risk a memory leak if `emplace()` throws. If you use `std::make_unique()` as the input, the pointer is always protected, and there is very little difference between using `insert()` and `emplace()`, so use whichever one you want. – Remy Lebeau Jul 17 '20 at 17:54
  • And if `insert()` throws (And I use `std::make_unique()`)? – Gordem Jul 17 '20 at 17:57
  • If you use `std::make_unique()` as the input, then the pointer is always protected at all times, there is no risk of leak, regardless of whether you use `insert()` or `emplace()` – Remy Lebeau Jul 17 '20 at 17:57
  • So, for safety it is better to use `insert()`? – Gordem Jul 17 '20 at 17:58
  • You can pass a `std::unique_ptr` to `emplace()` too, and be just as safe. It will simply move-construct its own `std::unique_ptr` that takes ownership of your pointer. Same with `insert()`. That is why for types that hold pointers to data, in light of move semantics, there is very little difference between `insert()` and `emplace()`. – Remy Lebeau Jul 17 '20 at 18:00

1 Answers1

2

The way unique_ptr was implemented ( move-only, not copy ) it prevents that this scenario you are concerned. But create others:

s.insert( std::make_unique<X>(1) ); // SAFE

auto p2 = std::make_unique<X>(2);
s.insert( std::move(p2) ); // also safe

auto p3 = std::make_unique<X>(3); 
//s.insert( p3 ); // unsafe, compiler complains

s.emplace( std::make_unique<X>(4) ); // SAFE
auto p5 = std::make_unique<X>(5);
s.emplace( std::move(p5) ); // also safe

auto p6 = std::make_unique<X>(6);
//s.emplace( p6 );  // unsafe on exception, compiler will complain if you uncomment

auto p7 = std::make_unique<X>(7);
s.emplace( std::move(p7) ); // also safe
s.emplace( std::move(p7) ); // insert same agains also "safe", but inserts "null"
s.emplace( std::move(p2) ); // insert "null" again, but nulls are highlanders here 

https://godbolt.org/z/3Gfoo7

Does not matters if you we inserted or emplaced it always happens thru move semantics, even when you s.insert( std::make_unique<X>(1) ), that is a move.

In this example, 3 and 6 never entered the set and, even after you move it twice like in the last two lines p7 or p2 in the sample, they will be "null" just after inserted/emplaced in the set.