我有以下代码:
#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编译
答案 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处再次构造。 —完示例]
GCC错误报告中的评论表明它显然是错误。
现在是2013年,因此如果您的析构函数可以抛出异常,那么明智的选择就是不要按值返回。
另一个用户:
是的,我注意到,Clang也有一个针对他们的错误,该错误已困扰了多年。但是,这种行为是错误的。