Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exceptionsafety of make_unique: Why is f(new T) exception safe

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.

like image 666
ted Avatar asked Dec 11 '22 23:12

ted


1 Answers

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:

  1. new A
  2. new B // might throw!
  3. Construct unique_ptr<A>
  4. Construct unique_ptr<B>
  5. Call 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:

  1. Call make_unique<A>
  2. Call make_unique<B>
  3. Call do_work

or

  1. Call make_unique<B>
  2. Call make_unique<A>
  3. Call do_work

making the leak where there are new'd objects floating around without owning unique_ptrs not possible.

like image 119
Billy ONeal Avatar answered May 16 '23 07:05

Billy ONeal