Reputation: 4680
When I need to have a data member which is a type of std::unique_ptr<A>
, then I have usually used std::unique::reset()
to initialize this unique_ptr
with a new object.
The following is a simplified example:
class A {
public:
void SetValue(int x) {
data_.reset(new B(x));
}
private:
std::unique_ptr<B> data_;
};
In the code review, one reviewer mentioned that it is a bad habit and he asked me not to use reset()
if possible. Instead, he suggested to use the following methods:
std::make_unique
or a template function like the following:
template <typename T>
struct MakeUniqueResult {
using scalar = std::unique_ptr<T>;
};
template <typename T, typename... Args>
typename internal::MakeUniqueResult<T>::scalar
MakeUnique(Args&&... args) {
return std::unique_ptr<T>(
new T(std::forward<Args>(args)...));
}
Are there some special reasons to avoid using std::unique_ptr::reset()
in the above case?
Upvotes: 10
Views: 2732
Reputation: 48038
In your example, the effect is the same whether you use std::make_unique
or std::unique_ptr::reset
with new.
But in more interesting code, using std::make_unique
solves some exception safety issues, and that's why your code reviewer is suggesting to make it a habit.
Consider what happens if you try to create two unique pointers at once:
Frobnicate(std::unique_ptr<Foo>(new Foo), std::unique_ptr<Bar>(new Bar(3)));
The compiler has to do a bunch of work to create those two parameters, and it has a lot of flexibility in the order it can do that work. If one of the constructors throws an exception, the memory allocated for the other one may or may not be cleaned up.
But if you use std::make_unique
:
Frobnicate(std::make_unique<Foo>(), std::make_unique<Bar>(3));
The temporary unique_ptrs will immediately own their respective objects and thus be able to clean them up if creating the other one throws an exception.
For exception safety, and the general guideline of avoiding using new and delete directly, it's a good habit to use std::make_unique
every time.
Upvotes: 11