函数返回值是不是是自动对象并因此保证被破坏?
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++ 语言有很多惊喜。以上是关于函数返回值是不是是自动对象并因此保证被破坏?的主要内容,如果未能解决你的问题,请参考以下文章