函数返回值是自动对象,因此可以保证被破坏吗?

时间:2016-01-08 09:25:59

标签: c++ exception-handling return return-value language-lawyer

在[except.ctor]中,标准(N4140)保证:

  

...调用析构函数   自try块以来构造的所有自动对象   进入...

但是在下面的示例中,空output证明函数foo的返回值没有被破坏,尽管它已被构造。使用g ++(5.2.1)和clang ++(3.6.2-1)以及选项-O0 -fno-elide-constructors -std=c++14编译。

struct A { ~A() { cout << "~A\n"; } };

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

A foo() {
  B b;
  return {};
}

int main() {
  try { foo(); }
  catch (...) { }
}

这是g ++和clang ++中的错误,还是函数返回值 被认为是自动对象,还是C ++语言中的循环漏洞?

在[stmt.return],[expr.call]或[dcl.fct]中都没有找到 明确声明函数返回值是否被视为自动 宾语。我找到的最接近的提示是6.3.3 p2:

  

......退货声明可以   涉及临时物体的建造和复制或移动......

和5.2.2 p10:

  

如果结果类型是左值,则函数调用是左值   引用类型或函数类型的右值引用,如果是,则为xvalue   result类型是对象类型的右值引用,否则是prvalue。

3 个答案:

答案 0 :(得分:45)

函数返回值被认为是临时值,并且在破坏本地之前对返回值的构造进行排序。

不幸的是,标准中没有详细说明。有open defect描述了这一点,并提供了一些解决问题的措辞

  

[...]具有void类型的操作数的return语句只能在返回类型为cv void的函数中使用。带有任何其他操作数的return语句只能用于返回类型不是cv void的函数中; return语句初始化要从操作数复制初始化(8.5 [dcl.init])返回的对象或引用。 [...]

     

返回实体的复制初始化在由return语句的操作数建立的全表达式结束时临时销毁之前进行排序,然后在本地变量的销毁之前对其进行排序(6.6 [stmt.jump])包含return语句的块。

由于函数返回值是临时值,因此在帖子开头的destructors are invoked for all automatic objects引号中不会涵盖它们。但是,[class.temporary]/3说:

  

[...]临时对象作为评估全表达式的最后一步被销毁,该表达式(词法上)包含创建它们的点。 即使该评估以抛出异常结束,也是如此。 [...]

所以我认为你可以认为这是GCC和Clang中的一个错误。

不要从析构函数中抛出;)

答案 1 :(得分:7)

这是一个错误,一次,MSVC实际上是正确的:它打印&#34; ~A&#34;。

答案 2 :(得分:7)

我修改了你的代码,我认为现在从输出我们可以看到A没有被破坏。

#include<iostream>

using namespace std;

struct A {
    ~A() { cout << "~A\n"; }
    A() { cout << "A()"; }
};

struct B {
    ~B() noexcept( false ) { cout << "~B\n"; throw(0); }
    B() { cout << "B()"; }
};

A foo() {
    B b;
    return;
}

int main() {
    try { foo(); }
    catch (...) {}
}

输出是:

  

B()A()〜乙

所以是的,它可能是一个错误。