17

对于这个程序

#include <iostream>
using std::cout;

struct C 
{
    C() { cout << "Default C called!\n"; }
    C(const C &rhs) { cout << "CC called!\n"; }
};

const C f()
{
    cout << "Entered f()!\n";
    return C();
}

int main()
{
    C a = f();
    C b = a;

    return 0;
}

我得到的输出是:

Entered f()!
Default C called!
CC called!

由于f()是按值返回,它应该返回一个临时的。照原样,T a = x;T a(x);不会调用复制构造函数来构造a,并将临时传入作为其参数吗?

4

3 回答 3

14

由于f()是按值返回,它应该返回一个临时的。照原样,T a = x;T a(x);不会调用复制构造函数来构造a,并将临时传入作为其参数吗?

查找返回值优化。这是默认开启的。如果您在使用 MSVC 2005+ 的 Windows 上,您可以/Od将其关闭并获得所需的结果(或-fno-elide-constructors在 GCC 上)。此外,对于 MSVC,请参阅本文

12.8 复制类对象

15当满足某些条件时,允许实现省略类对象的复制构造,即使对象的复制构造函数和/或析构函数有副作用。在这种情况下,实现将省略的复制操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本应被销毁的较晚时间优化。115 这种复制操作的省略在以下情况下是允许的(可以组合起来以消除多个副本):

在具有类返回类型的函数的 return 语句中, 当表达式是与函数返回类型具有相同 cv 非限定类型的非易失性自动对象的名称时,可以通过构造自动对象来省略复制操作对象直接放入函数的返回值中——在 throw 表达式中,当操作数是非易失性自动对象的名称时,可以通过直接构造自动对象来省略从操作数到异常对象 (15.1) 的复制操作进入异常对象

— 当一个未绑定到引用(12.2)的临时类对象将被复制到具有相同 cv-unqualified 类型的类对象时,可以通过将临时对象直接构造到省略的目标中来省略复制操作复制

— 当异常处理程序的异常声明(第 15 条)声明与异常对象(15.1)具有相同类型的对象(除了 cv 限定)时,可以通过将异常声明视为如果程序的含义将保持不变,则异常对象的别名,除非为异常声明声明的对象执行构造函数和析构函数。

注意:强调我的

于 2010-02-24T02:19:57.550 回答
4

这是您的编译器支持的返回值优化 (RVO)功能的示例。

按值返回时可能不会调用复制构造函数。

使用-fno-elide-constructorsGCC 上的选项来关闭该功能。

于 2010-02-24T02:18:54.053 回答
2

我相信这叫做返回值优化

我假设当f()返回C对象时,该对象被分配在调用方法的堆栈空间中,因此不需要副本来初始化C a。这是你的default C called

C b = a

这会导致复制构造函数,因此您的CC called.

顺便说一句,wiki 上的示例看起来与您的代码非常相似。

于 2010-02-24T02:24:58.643 回答