29

cppref中,在 C++17 之前,以下内容一直有效:

诸如此类的代码如果在调用之后并引发异常,f(std::shared_ptr<int>(new int(42)), g())可能会导致内存泄漏,而这是安全的,因为两个函数调用永远不会交错。gnew int(42)f(std::make_shared<int>(42), g())

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

4

2 回答 2

20

函数参数的评估顺序由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(),则甚至没有内存分配。

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

于 2018-02-17T17:43:50.530 回答
20

P0145R3论文(C++17 接受)改进了几个 C++ 构造的评估顺序,包括

后缀表达式从左到右计算。这包括函数调用和成员选择表达式

具体来说,本文在标准的 5.2.2/4 段中添加了以下文本:

后缀表达式在表达式列表中的每个表达式和任何默认参数之前排序。与参数初始化相关的每个值计算和副作用,以及初始化本身,在每个与任何后续参数初始化相关的值计算和副作用之前进行排序。

于 2018-02-17T14:43:58.090 回答