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?
Asked
Active
Viewed 869 times
2

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 Answers
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
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.

Cleiton Santoia Silva
- 473
- 2
- 7
-
1
-
Just like any other value, you can have only one null in a set< unique_ptr<> > – Cleiton Santoia Silva Jul 17 '20 at 20:48