I have been reading GOTW102, and wonder, why make_unique
is more exception safe than the other cases, or in detail why f(new T(...))
is more exception safe than f(new T1(...), new T2(...))
.
The make_unique
implementation from the blog reads:
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
Now I wonder if f(new T(...))
is in general exception safe (leak free), or if it is just exception safe in the case of make_unique
because of the added knowledge that the constructor of std::unique_ptr
does not throw? (Since if it would the freshly constructed T
would be leaked anyways according to my understanding.
The reason is that in a function call or similar, the arguments do not induce sequence points (are not "sequenced before"). For example:
do_work(unique_ptr<A>(new A), unique_ptr<B>(new B));
The compiler is allowed to generate code that looks like:
new A
new B
// might throw!unique_ptr<A>
unique_ptr<B>
do_work
If new B
throws, then you've leaked the A
, because no unique_ptr
was ever constructed.
Putting the unique_ptr
construction into its own function eliminates this problem, because compilers aren't allowed to execute function bodies concurrently (so the "new" and "construct unique_ptr
steps need to be done together).
That is, given:
do_work(make_unique<A>(), make_unique<B>())
The compiler must generate code that looks like:
make_unique<A>
make_unique<B>
do_work
or
make_unique<B>
make_unique<A>
do_work
making the leak where there are new'd objects floating around without owning unique_ptr
s not possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With