函数返回值是不是是自动对象并因此保证被破坏?

Posted

技术标签:

【中文标题】函数返回值是不是是自动对象并因此保证被破坏?【英文标题】:Are function return values automatic objects and thus guaranteed to be destructed?函数返回值是否是自动对象并因此保证被破坏? 【发布时间】:2016-04-12 22:10:54 【问题描述】:

在 [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:

...return 语句可以 涉及临时对象的构造和复制或移动...

和 5.2.2 p10:

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

【问题讨论】:

【参考方案1】:

我修改了你的代码,我认为现在从输出中我们可以看到 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()~B

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

【讨论】:

【参考方案2】:

函数返回值被认为是临时的,返回值的构造在locals的销毁之前排序。

不幸的是,标准中没有详细说明这一点。有一个 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 中的错误。

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

【讨论】:

我发现 gcc 和 clang 多年来都针对他们提出了这个错误,所以我不希望他们会很快修复它:gcc、clang。 在这种情况下允许编译器省略对象A的构造吗?我的意思是一些类似于 RVO 的规则。 @Mikhail 我不这么认为。 RVO不能完全消除构造,它可以消除中间构造。【参考方案3】:

这是一个错误,这一次,MSVC 确实做到了:它打印“~A”。

【讨论】:

有时会看到 clang 和 gcc 拒绝代码,但 MSVC 接受具有定义行为的代码。我也遇到过一次,如果有人问我可以举个例子。 能否请您添加对标准的引用,以证明函数返回值被视为自动对象,或者以其他方式证明它确实是 gcc 和 clang 中的错误? @FlorianKaufmann 我认为this example 表明有一个A 对象永远不会为其调用析构函数,即使它在创建后超出了范围。 @FlorianKaufmann TartanLlma 对您帖子下方的缺陷报告的引用似乎完全解决了手头的问题(是在局部变量 b 之前构造的 A 类型的待返回临时对象是破坏?)。答案应该是肯定的,但没有明确说明。答案可能是肯定的,因为本地人参与将返回值放在一起的明显用例。 @MSalters。 “还能是什么?”没错,我也有同样的感受。然而,这不是证据。我更喜欢确认并查看标准中的相关段落。我们从经验中知道,C++ 语言有很多惊喜。

以上是关于函数返回值是不是是自动对象并因此保证被破坏?的主要内容,如果未能解决你的问题,请参考以下文章

来自 C++ 的回调 python 函数,对象被破坏

构造函数和构析函数

构造函数主要特点

面向对象----方法的重载

JavaScript中使用new操作符实例化对象时构造函数有返回值的情况分析

析构函数和构造函数