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

Evg*_*eny 38 c++ destructor exception object-lifetime c++14

我有以下代码:

#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(...) {}
}
Run Code Online (Sandbox Code Playgroud)

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

 OK1 born
 OK2 born
 OK3 born
 OK2 gone
 OK1 gone
Run Code Online (Sandbox Code Playgroud)

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

编辑:

用gcc 6.3编译

P.W*_*P.W 23

根据标准,这种行为是错误的,这已经在问题的评论部分中提到过.这在" 异常处理 "一节中说明.

按照该缺陷报告open-std.org,他们已经意识到,实现(GCC和锵)是错误的这一点,早在2015年9月28日.但提议的决议仅在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
  }
Run Code Online (Sandbox Code Playgroud)

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

GCCClang都有针对这个问题提出的错误.

关于GCC错误报告的评论表明它显然是一个错误.

乔纳森威克利评论:

它现在是2013年所以,如果您的析构函数可以抛出,那么明智的做法就是不返回值.

另一个用户:

是的,我注意到了,而Clang也有一个针对他们的错误,这些错误已经萎缩多年.然而,这种行为是错误的.