Безопасность исключений и make_unique
Просто для пояснения, использование make_unique
добавляет только безопасность исключений, когда у вас есть несколько распределений в выражении, а не только один, правильный? Например
void f(T*);
f(new T);
является абсолютно безопасным исключением (в отношении распределений и прочее), а
void f(T*, T*);
f(new T, new T);
нет, правильно?
Ответы
Ответ 1
Не только когда у вас есть несколько распределений, но и когда вы можете бросить в разных местах. Рассмотрим это:
f(make_unique<T>(), function_that_can_throw());
Versus:
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
, и он не оказывает никакого влияния на производительность, поэтому нет причин не использовать его (хотя на самом деле он не используется, он вводит много ошибок).
Ответ 2
Я бы подумал, что вам лучше сравнивать вещи с помощью 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
до построения любого из временных рядов).
Ответ 3
Как и в случае с С++ 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
Это означает, что теперь это безопасно.