std::make_shared() 在 C++17 中的变化

std::make_shared() change in C++17

cppref 中,以下内容在 C++17 之前成立:

code such as f(std::shared_ptr<int>(new int(42)), g()) can cause a memory leak if g gets called after new int(42) and throws an exception, while f(std::make_shared<int>(42), g()) is safe, since two function calls are never interleaved.

我想知道 C++17 中引入的哪个更改使它不再适用。

C++17 中的 P0145R3 paper (which was accepted) 细化了几个 C++ 构造的计算顺序,包括

Postfix expressions are evaluated from left to right. This includes functions calls and member selection expressions

具体来说,论文在标准的5.2.2/4段中增加了如下文字:

The postfix-expression is sequenced before each expression in the expression-list and any default argument. Every value computation and side effect associated with the initialization of a parameter, and the initialization itself, is sequenced before every value computation and side effect associated with the initialization of any subsequent parameter.

函数参数的求值顺序被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()之后,甚至没有内存分配。

无论哪种情况,无论如何都不会再次发生内存泄漏。