Я читал GOTW102, и удивляюсь, почему make_unique
безопаснее исключения, чем другие случаи, или подробно, почему f(new T(...))
безопаснее исключения, чем f(new T1(...), new T2(...))
,
make_unique
Реализация из блога гласит:
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
Теперь мне интересно, если f(new T(...))
как правило, безопасен для исключений (без утечек), или если он является безопасным только для исключений в случае make_unique
из-за дополнительных знаний, что конструктор std::unique_ptr
не бросает? (Так как если бы это был недавно построенный T
будет протекать в любом случае в соответствии с моим пониманием.
Причина в том, что при вызове функции или подобном аргументы не вызывают точки последовательности (не «секвенируются раньше»). Например:
do_work(unique_ptr<A>(new A), unique_ptr<B>(new B));
Компилятору разрешено генерировать код, который выглядит следующим образом:
new A
new B
// может бросить!unique_ptr<A>
unique_ptr<B>
do_work
Если new B
бросает, то вы слили A
, потому что нет unique_ptr
был когда-либо построен.
Ввод unique_ptr
конструирование в свою собственную функцию устраняет эту проблему, потому что компиляторам не разрешается выполнять тела функций одновременно (таким образом, конструкции «new» и » unique_ptr
шаги должны быть сделаны вместе).
То есть дано:
do_work(make_unique<A>(), make_unique<B>())
Компилятор должен генерировать код, который выглядит следующим образом:
make_unique<A>
make_unique<B>
do_work
или же
make_unique<B>
make_unique<A>
do_work
утечка, где есть новые объекты, плавающие вокруг без владения unique_ptr
не возможно