std :: make_shared() изменение в С++ 17

В cppref выполняется следующее действие до С++ 17:

код, такой как f(std::shared_ptr<int>(new int(42)), g()) может вызвать утечку памяти, если g вызывается после new int(42) и генерирует исключение, тогда как f(std::make_shared<int>(42), g()) безопасен, поскольку два вызова функций никогда не чередуются.

Мне интересно, какое изменение, внесенное в С++ 17, делает это более неприменимым.

Ответ 1

Порядок оценки аргументов функции изменяется на P0400R0.

Перед изменением оценка аргументов функции не зависит от друг друга. Это означает, что оценка g() может быть вставлена в оценку std::shared_ptr<int>(new int(42)), что приводит к ситуации, описанной в вашем цитированном контексте.

После изменения оценка аргументов функции неопределенно секвенирована без перемежения, что означает, что все побочные эффекты std::shared_ptr<int>(new int(42)) имеют место либо до, либо после g(). Теперь рассмотрим случай, когда g() может бросать.

  • Если все побочные эффекты std::shared_ptr<int>(new int(42)) имеют место перед символами g(), выделенная память будет освобождена деструктором std::shared_ptr<int>.

  • Если все побочные эффекты std::shared_ptr<int>(new int(42)) происходит после тех, что у g(), выделения памяти даже нет.

В любом случае утечки памяти еще нет.

Ответ 2

В документе P0145R3 (который был принят в C++ 17) уточняется порядок оценки нескольких конструкций C++, в том числе

Постфиксные выражения оцениваются слева направо. Сюда входят вызовы функций и выражения выбора членов

В частности, документ добавляет следующий текст к пункту 5.2.2/4 стандарта:

Постфикс-выражение секвенируется перед каждым выражением в списке выражений и любым аргументом по умолчанию. Каждое вычисление значения и побочный эффект, связанные с инициализацией параметра и самой инициализацией, секвенируются перед каждым вычислением значения и побочным эффектом, связанным с инициализацией любого последующего параметра.