Просто чтобы уточнить, используя make_unique
только добавляет безопасность исключений, когда у вас есть несколько распределений в выражении, а не только одно, правильно? Например
void f(T*);
f(new T);
совершенно безопасен в отношении исключений (в отношении распределений и прочего), в то время как
void f(T*, T*);
f(new T, new T);
не является правильным?
Не только когда у вас есть несколько распределений, но и когда вы можете бросить в разных местах. Учти это:
f(make_unique<T>(), function_that_can_throw());
Против:
f(unique_ptr<T>(new T), function_that_can_throw());
Во втором случае компилятору разрешено вызывать (по порядку):
new T
function_that_can_throw()
unique_ptr<T>(...)
Очевидно, если function_that_can_throw
на самом деле бросает, то вы утечки. make_unique
предотвращает этот случай.
И, конечно же, второе распределение (как в вашем вопросе) — это особый случай function_that_can_throw()
,
Как правило, просто используйте make_unique
так что ваш код соответствует. Это всегда правильно (читай: исключение безопасно), когда вам нужно unique_ptr
и это не влияет на производительность, поэтому нет никаких причин не использовать его (хотя на самом деле не использование его вводит много ошибок).
Я думаю, что вам будет лучше сравнивать вещи на самом деле, используя std::unique_ptr<T>
:
void f(std::unique_ptr<T>);
f(std::unique_ptr<T>(new T));
f(std::make_unique<T>());
Ни один из этих вызовов не может просочиться, если возникает исключение. тем не мение
void f(std::unique_ptr<T>, std::unique_ptr<T>);
g(std::unique_ptr<T>(new T), std::unique_ptr<T>(new T));
g(std::make_unique<T>(), std::make_unique<T>());
В этом случае версия использует std::unique_ptr<T>
явно может просочиться, если выдается исключение (потому что компилятор может начать оценку new
-выражения перед построением любого из временных).
Начиная с C ++ 17 проблема безопасности исключений была исправлена перепиской [Expr.call]
Инициализация параметра, включая каждое связанное с ним вычисление значения и побочный эффект, определяется неопределенным образом относительно последовательности любого другого параметра.
Вот неопределенно последовательность означает, что одно упорядочено перед другим, но не указано, которое.
f(unique_ptr<T>(new T), function_that_can_throw());
Может иметь только два возможных порядка исполнения
new T
unique_ptr<T>::unique_ptr
function_that_can_throw
function_that_can_throw
new T
unique_ptr<T>::unique_ptr
Что означает, что теперь это безопасно.