是否定义了函数参数中的内部执行顺序?

Pat*_*ryk 0 c++ operator-precedence dynamic-memory-allocation c++11

我们假设我们有以下代码片段:

void foo(std::unique_ptr<Data> p1, std::unique_ptr<Data> p2){
    bar(p1);
    bar(p2);
}

int main(){
    foo( std::unique_ptr<Data>{new Data}, std::unique_ptr<Data>{new Data});
}
Run Code Online (Sandbox Code Playgroud)

问题是:运行此代码时是否会释放内存(无论发生什么情况)?

标准说我们不能依赖语句的顺序作为函数参数,但内部函数调用/内存分配等呢?它在这里重要吗?

Bar*_*rry 7

在运行此代码时将永远释放内存(无论发生什么)?

在C++之前17:不.一个潜在的执行顺序是:

  1. 剩下 new Data
  2. new Data
  3. unique_ptr构造函数
  4. 正确的unique_ptr构造函数

如果(1)抛出,我们没事.如果(2)抛出,我们还没有实际运行unique_ptr构造函数,所以我们没有任何清理机制来从(1)中释放内存.那会泄漏.只有在(1)和(2)投掷都不是我们罚款的情况下.

这就是标准介绍的原因std::make_unique:

foo( std::make_unique<Data>(), std::make_unique<Data>() );
Run Code Online (Sandbox Code Playgroud)

没有这个问题 - new现在s内部分组到unique_ptr构造函数.因此,如果一个人成功,那么它已经被包裹在其RAII后卫中.


在C++ 17之后:是的.虽然函数参数的评估顺序仍未指定,但是有一条新规则,即此类评估不能交错.也就是说,f(g(a), h(b))潜在的评估顺序是:[f, g, a, h, b][f, h, b, g, a].它不再是可以评估都ab之前gh,这与原代码的潜在问题.

  • @ user3853544 - 不,抛出*构造函数*完全不是**.投掷实际上是你可以做的最好的事情从ctor报告错误,所以你的ctor可能会失败,它应该扔.(通常不鼓励从*析构者*投掷.) (6认同)
  • @ user3853544异常被添加到C++ _precisely,以便能够从构造函数_报告错误(因为它们没有可用于返回错误的返回值). (2认同)