鉴于下面的代码,我无法理解gcc中的输出。我希望创建和销毁两个对象,但只能看到对构造函数和析构函数的一次调用。这里发生了什么?
#include <string>
#include <iostream>
struct Huge{
Huge() { std::cout << "Constructor" << std::endl; }
Huge(Huge const &r) { std::cout << "Copy Constructor" << std::endl; }
~Huge() { std::cout << "Destructor" << std::endl; }
};
Huge g() {
std::cout << "Entering g" << std::endl;
Huge temp;
std::cout << "Exiting g" << std::endl;
return temp;
}
int main(){
Huge h2(g());
std::cout << "Before leaving main" << std::endl;
}
g ++(4.4)中此代码的输出是
输入g
构造
退出g
离开主要
之前析构
答案 0 :(得分:5)
是的,这是通过 Named Return Value Optimization 复制省略。
C ++标准允许实现省略由return语句产生的复制操作,即使复制构造函数有副作用。
参考:
C ++ 03标准:
12.8复制类对象:
#15
当满足某些条件时,允许实现省略类对象的复制结构,即使该对象的复制构造函数和/或析构函数具有副作用。 在这种情况下,实现将省略的复制操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象具有的时间的晚期。在没有优化的情况下被销毁 .111)允许复制操作的省略 以下情况(可以合并以消除多个副本):
- 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cv-unqualified类型的非易失性自动对象的名称时,可以省略复制操作将自动对象直接构造为函数的返回值
- 当一个未绑定到引用(12.2)的临时类对象被复制到具有相同cv-nonqualified类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制操作省略的副本
答案 1 :(得分:1)
C ++允许避免在像你这样的情况下创建和复制额外的对象。这称为命名返回值优化。关键是你肯定知道返回后对象temp
无论如何都会消失,复制构造函数的语义应该是与原始对象完全等效的复制。
请注意,实际上这里有两个优化。如果没有优化,首先会将对象temp
复制到g
中的返回值中,然后将返回值复制到h2
中的main
。命名返回值优化将副本删除为返回值。从返回值复制到h2
被省略,因为返回值是临时对象,在这里,也可以省略副本的创建。
请注意,与其他优化不同,即使这些优化改变了可观察的行为(如在测试程序中),也允许这些优化。这是因为否则这些优化无法在很多情况下执行,因为它没有任何区别(实际上,除了调试输出之外,这应该永远不会在编写良好的程序中产生差异),因为编译器通常无法证明elision不会改变可观察的行为。另一方面,无法手动删除副本,因此编译器能够自动执行此操作非常重要。
最终发生的是对象temp
直接在空间h2
中占用,因此在返回语句h2
的点处已经包含正确的值。换句话说,由于优化temp
和h2
实际上是同一个对象。