复制构造函数省略?

4

可能是重复问题:
为什么析构函数只被调用了一次?

给定下面的代码,我无法理解在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

离开main之前

析构函数


遗憾的是,标记为重复的答案中没有引用标准,该标准以简单(换句话说)和清晰的语言解释了复制省略语义。 - Alok Save
2个回答

6

是的,这是通过命名返回值优化实现的复制省略。

C++标准允许实现在返回语句中省略复制操作,即使复制构造函数具有副作用。

参考:

C++03标准:
12.8 复制类对象:

# 15

当满足某些条件时,实现允许省略类对象的复制构造,即使该对象的复制构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制操作的源和目标视为仅是指称同一对象的两种不同方式,并且该对象的销毁发生在两个对象会在没有优化的情况下销毁的时间之后。111)可以在以下情况下允许省略复制操作(这些情况可以组合以消除多个复制):
- 在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型具有相同cv-unqualified类型的非易失自动对象的名称时,可以通过直接将自动对象构造到函数的返回值中来省略复制操作 - 当未绑定到引用(12.2)的临时类对象将被复制到具有相同cv-unqualified类型的类对象时,可以通过直接将临时对象构造到省略复制的目标中来省略复制操作

有什么想法为什么我看不到对两个构造函数和析构函数的调用(一个是'temp'的,另一个是'h2'的)? - Chubsdad
@Chubsdad:就像答案所说的那样,编译器可以自由地优化所有副本(从 tempg() 的返回值以及从返回值到 h2 的副本),并且只构造一个对象。 - R. Martinho Fernandes
通过将自动对象直接构建到函数的返回值中,这并不意味着只创建了一个对象。其中一个是“temp”,另一个是“h2”。 - Chubsdad
@Chubsdad:不是的。temp 直接构造到了 h2 中。 - R. Martinho Fernandes
@Chubsdad:我在标准引用中加粗了回答你问题的相关部分。 - Alok Save
值得注意的是,为了测试目的,它通常可以使用命令行标志关闭。 - Bingo

2

C++允许在像你这样的情况下避免创建和复制额外的对象。这被称为命名返回值优化。关键是你确定在返回后,对象temp无论如何都会消失,并且复制构造函数的语义应该是制作与原始对象完全相同的副本。

请注意,实际上这里发生了两个优化。如果没有优化,对象temp将首先被复制到g中的返回值中,然后将返回值复制到main中的h2中。命名返回值优化省略了复制到返回值中的复制。从返回值复制到h2的复制被省略,因为返回值是一个临时对象,在这里,复制的创建也可能被省略。

请注意,与其他优化不同,即使它们改变了可观察行为(如您的测试程序中),这些优化也是允许的。这是因为否则在许多情况下无法执行这些优化,即使没有任何区别(实际上,除了调试输出之外,在编写良好的程序时,这应该永远不会有任何区别),因为编译器通常无法证明省略不会改变可观察行为。另一方面,没有办法手动删除副本,因此编译器能够自动执行此操作非常重要。
最终发生的是对象temp直接创建在h2所占用的空间中,因此在返回语句h2处已经包含了正确的值。换句话说,由于优化temph2实际上是同一个对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接