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-if你wrote:
test o=std::move(test(5)); //equivalent to this
**我看到了这个问题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-if你wrote:
test o=std::move(test(5)); //equivalent to this