gpt4 book ai didi

c++ - 返回局部变量是否返回拷贝并销毁原始变量(nrvo)?

转载 作者:行者123 更新时间:2023-12-05 04:29:17 25 4
gpt4 key购买 nike

我看到了这个问题 When is an object "out of scope"?

我看了sparc_spread 的回答,发现其中有一个问题。在他的回答的这一部分:

Circle myFunc () {
Circle c (20);
return c;
}
// The original c went out of scope.
// But, the object was copied back to another
// scope (the previous stack frame) as a return value.
// No destructor was called.

他说过“没有调用析构函数。”但是当我尝试运行此代码(由我编写)时:

   /* Line number 1 */ #include <iostream>
/* Line number 2 */ #include <string>
/* Line number 3 */ using namespace std;
/* Line number 4 */ class test {
/* Line number 5 */ public:
/* Line number 6 */ test(int p) {
/* Line number 7 */ cout << "The constructor ( test(int p) ) was called"<<endl;
/* Line number 8 */ }
/* Line number 9 */ test(test&&c)noexcept {
/* Line number 10 */ cout << "The constructor ( test(test && c) ) was called" << endl;
/* Line number 11 */ }
/* Line number 12 */ ~test() {
/* Line number 13 */ cout << "The distructor was called" << endl;
/* Line number 14 */ }
/* Line number 15 */ };
/* Line number 16 */ test function() {
/* Line number 17 */ test i(8);
/* Line number 18 */ return i;
/* Line number 19 */ }
/* Line number 20 */ int main()
/* Line number 21 */ {
/* Line number 22 */ test o=function();
/* Line number 23 */ return 0;
/* Line number 24 */ }

输出:

The constructor ( test(int p) ) was called
The constructor ( test(test && c) ) was called
The distructor was called
The distructor was called

所以我的代码的输出显示:

  1. 调用了两个构造函数(这不是我要讨论的重点。所以我不会讨论(为什么、何时或如何)调用了两个构造函数?)

  2. 调用了两个析构函数

当我使用调试器(知道第一个析构函数何时被调用)时,我发现第一个析构函数在第 18 行被调用(我代码中的第 18 行)。

最后。我的观点对吗?

最佳答案

Does returning a local variable return a copy and destroy the original?

您问题的最终答案是,这取决于是否启用了优化。因此,让我们分别讨论每个案例。另请注意,由于原始问题中的给定输出是针对 C++17 的,因此下面的讨论也适用于相同的(C++17 及以上版本)。

优化

在这里我们将看到启用优化 (NRVO) 时会发生什么。

class test {
public:
test(int p) {
cout << "The constructor ( test(int p) ) was called: "<<this<<endl;
}
test(test&&c)noexcept {
cout << "The constructor ( test(test && c) ) was called: "<<this << endl;
}
~test() {
cout << "The distructor was called: "<<this << endl;
}
};
test function() {
test i(8);
return i;
}
int main()
{
test o=function();
return 0;
}

output该程序是(启用 NRVO):

The constructor ( test(int p) ) was called: 0x7fff78e42887   <-----object o construction
The distructor was called: 0x7fff78e42887 <-----object o destruction

可以使用称为命名返回值优化(aka NRVO)的优化来理解上述输出,如 copy elison 中所述。其中指出:

Under the following circumstances, the compilers are permitted, but not required to omit the copy and move (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:

  • In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".

(强调我的)

让我们将其应用于上面给出的示例并尝试理解输出。名为 i 的变量是一个局部变量,意味着它具有自动存储持续时间,因此根据上面引用的语句,允许编译器(但不是必需的!)直接将对象构造到变量的存储中名为 o。也就是说,它好像wrote :

test o(5); //equivalent to this due to NRVO

因此在这里我们首先看到对对象 o 的转换构造函数 test::test(int) 的调用,然后是对该对象 o 的析构函数调用

没有优化

您可以选择使用 -fno-elide-constructors 标志禁用此优化。当使用这个标志执行相同的程序时,output该计划将成为:

The constructor ( test(int p) ) was called: 0x7ffda9d94fe7        <-----object i construction
The constructor ( test(test && c) ) was called: 0x7ffda9d95007 <-----object o construction
The distructor was called: 0x7ffda9d94fe7 <-----object i destruction
The distructor was called: 0x7ffda9d95007 <-----object o destruction

这一次,因为我们向编译器提供了 -fno-elide-constructors 标志,所以 NRVO 被禁用。这意味着现在编译器不能省略 return 语句 return i; 对应的复制/移动构造。这反过来意味着首先对象 i 将使用转换构造函数 test::test(int) 构造,因此我们在输出中看到第一行。

接下来,这个名为 i 的局部变量将使用移动构造函数 test::test(test&&) 移动,因此我们看到输出的第二行。请注意,由于强制复制elison,对象o直接从这个移动的prvalue 直接构建,因为您使用的是C++17。

接下来,局部变量 i 将使用析构函数 test::~test() 析构,我们在输出中看到第三行。

最后,对象 o 将被销毁,我们看到输出的第四行。

在这种情况下,它是好像wrote :

test o = std::move(test(5)); //equivalent to this

关于c++ - 返回局部变量是否返回拷贝并销毁原始变量(nrvo)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72383114/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com