析构函数异常时返回值的破坏

时间:2018-11-06 07:29:32

标签: c++ exception c++14 destructor object-lifetime

我有以下代码:

#include <stdexcept>
#include <iostream>

struct ok {
    int _n;
    ok(int n) : _n(n) { std::cerr << "OK" << n << " born" << std::endl; }
    ~ok() {  std::cerr << "OK" << _n << " gone" << std::endl; }
};

struct problematic {
    ~problematic() noexcept(false) { throw std::logic_error("d-tor exception"); }
};

ok boo() {
    ok ok1{1};
    problematic p;
    ok ok2{2};
    return ok{3}; // Only constructor is called...
}

int main(int argc, char **argv) {
    try {boo();} catch(...) {}
}

我看到没有调用他的ok {3}的析构函数,输出为:

 OK1 born
 OK2 born
 OK3 born
 OK2 gone
 OK1 gone

这是C ++ 14的预期行为吗?

编辑

使用gcc 6.3编译

1 个答案:

答案 0 :(得分:23)

按照标准,此行为是错误的,并且在问题的注释部分中已提及。 这在Exception handling的部分中有所说明。

根据defect reports中的open-std.org,他们知道,最早在2015年9月28日,实现(GCC和Clang)是错误的。 但是拟议的解决方案仅在2016年2月发布,并且编译器(GCC和Clang)尚未包含此修复程序。

  

建议的解决方案(2016年2月):

     

将18.2 [except.ctor]第2段更改如下:
  自从进入try块以来,已构造但尚未销毁的每个类类型的自动对象都调用了析构函数。 如果在销毁针对返回语句的临时变量或局部变量的过程中引发异常(9.6.3 [stmt.return]),则会调用返回对象的析构函数(如果有)。对象按照其完成构造的相反顺序销毁。 [示例:

  struct A { };

  struct Y { ~Y() noexcept(false) { throw 0; } };

  A f() {
    try {
      A a;
      Y y;
      A b;
      return {};   // #1
    } catch (...) {
    }
    return {};     // #2
  }
     

在#1处,构造了返回的A型对象。然后,销毁局部变量b(9.6 [stmt.jump])。接下来,局部变量y被破坏,导致堆栈展开,导致返回对象的破坏,随后局部变量a的破坏。最后,返回的对象在#2处再次构造。 —完示例]

GCCClang中都存在针对此问题的错误。

GCC错误报告中的评论表明它显然是错误。

Jonathan Wakely评论:

  

现在是2013年,因此如果您的析构函数可以抛出异常,那么明智的选择就是不要按值返回。

另一个用户:

  

是的,我注意到,Clang也有一个针对他们的错误,该错误已困扰了多年。但是,这种行为是错误的。