returning 一个局部变量 return 是一个副本并销毁原始变量吗?

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

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

我看了Mr.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- 调用了两个破坏者

并且当我使用调试器(知道何时调用第一个 distructor)时,我发现第一个 distructor 在第 18 行被调用(我的代码中的第 18 行)。如果他(sparc_spread 先生)看到我的问题并同意他将编辑您的答案。

最后。我的观点对吗?

我很抱歉这个伟大的社区使用这种方式来表达我的观点,但我没有其他方式。 sparc_spread 先生,我为您感到抱歉,您是一位经验丰富的人。请不要针对这个问题提出问题。

我还有一个问题要问你(sparc_spread先生)

当您(sparc_spread 先生)说“所以这是堆栈帧(函数的执行)和其中声明的局部变量的经典示例,一旦超出范围堆栈框架退出 - 一旦函数完成:“

在这部分:“一旦堆栈帧退出”

您的意思是:“一旦程序退出该帧”?

如果您的回答是肯定的,请同时在您的回答中编辑此部分

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

可以使用称为 return 值优化(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 的变量是一个局部变量,这意味着它具有自动存储持续时间,因此根据上面引用的语句,允许编译器(但不是必需的!)直接将对象构造到名为 [= 的变量的存储中16=]。也就是说,好像wrote:

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

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

的析构函数调用

没有优化

您可以选择 禁用 此优化,方法是使用 -fno-elide-constructors 标志。而当用这个flag执行同一个程序时,程序的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; 对应的 copy/move 构造。这反过来意味着首先将使用转换构造函数 test::test(int) 构造对象 i,因此我们在输出中看到第一行。

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

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

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

这样的话,就是as-ifwrote:

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