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

Flo*_*ann 56 c++ exception-handling return return-value language-lawyer

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

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

但是在下面的例子中,空输出证明函数的返回值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 (...) { }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

和5.2.2 p10:

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

Tar*_*ama 45

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

不幸的是,标准中没有详细说明.有一个开放的缺陷描述了这一点并提供了一些解决问题的措辞

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

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

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

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

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

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

  • 我发现gcc和clang已经有多年来一直存在这个bug,所以我不希望他们很快就会解决这个问题:[gcc](https://gcc.gnu.org/bugzilla/show_bug.cgi? id = 33799),[clang](https://llvm.org/bugs/show_bug.cgi?id=12286). (6认同)

Seb*_*edl 7

这是一个错误,一次,MSVC实际上是正确的:它打印"~A".

  • 你能不能添加对标准的引用,证明函数返回值被认为是一个自动对象,或者哪个证明它确实是gcc和clang中的错误? (6认同)
  • @MSalters."还能做什么呢?" 对,我有同样的感受.但它不是证据.我更愿意确认并查看标准中的相关段落.我们从经验中知道C++语言有很多惊喜. (4认同)

AC *_*age 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 (...) {}
}
Run Code Online (Sandbox Code Playgroud)

输出是:

B()A()〜乙

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