Подтвердить что ты не робот

Безопасность исключений и make_unique

Просто для пояснения, использование make_unique добавляет только безопасность исключений, когда у вас есть несколько распределений в выражении, а не только один, правильный? Например

void f(T*);

f(new T);

является абсолютно безопасным исключением (в отношении распределений и прочее), а

void f(T*, T*);

f(new T, new T);

нет, правильно?

4b9b3361

Ответ 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

Это означает, что теперь это безопасно.