初始化具有函数返回值的对象时未复制的复制构造函数

时间:2014-11-14 19:16:03

标签: c++ copy-constructor temporary-objects return-value-optimization copy-elision

请考虑以下代码:

#include <iostream>

using namespace std;

class A
{
    public:
        int a;
        A(): a(5)
        {
           cout << "Constructor\n";
        }
        A(const A &b)
        {
            a = b.a;
            cout << "Copy Constructor\n";
        }
        A fun(A a)
        {
            return a;
        }
};

int main()
{
    A a, c;
    A b = a.fun(c);
    return 0;
}

以上代码g++ file.cpp的输出为:

Constructor
Constructor
Copy Constructor
Copy Constructor

以上代码g++ -fno-elide-constructors file.cpp的输出为:

Constructor
Constructor
Copy Constructor
Copy Constructor
Copy Constructor

我知道返回值优化。我的问题是哪个复制构造函数的调用被删除(返回期间的临时对象或被复制到b的返回对象)?

如果省略的复制构造函数是用于创建b的构造函数,那么如何创建b(因为在这种情况下也没有构造函数调用)?

如果我用A b = a.fun(c);替换行a.fun(c)并使用第一种方法甚至第二种方法进行编译,那么复制构造函数也会被调用2次。因此,如果在上一段中解释的情况下,临时对象的复制构造函数被省略了,那么为什么在这种情况下它没有被省略呢?

2 个答案:

答案 0 :(得分:6)

#include <iostream>

using namespace std;

class A
{
public:
    int a;
    A(): a(5)
    {
        cout << "Constructing: " << (void *)this << std::endl;
    }
    A(const A &b)
    {
        a = b.a;
        cout << "Copy Constructor: " << (void *)this << " from " << (void *)&b << std::endl;
    }
    A fun(A a)
    {
        return a;
    }
};

int main()
{

    A a, c;
    A b = a.fun(c);

    std::cout << "a:" << (void *)&a << std::endl <<
              "b:" << (void *)&b << std::endl <<
              "c:" << (void *)&c << std::endl;
    return 0;
}

收率:

Constructing: 0x7fffbb377220
Constructing: 0x7fffbb377210
Copy Constructor: 0x7fffbb377230 from 0x7fffbb377210
Copy Constructor: 0x7fffbb377200 from 0x7fffbb377230
a:0x7fffbb377220
b:0x7fffbb377200
c:0x7fffbb377210

因此它构造a,构造c,将c复制到函数的中间(参数a),然后将中间体直接复制到{{1跳过典型的a复制到返回中间体。如果您按值传递(更改为b

),则可以更好地证明这一点
A fun(const A& a)

a构造,c构造,c直接复制到b,尽管b没有传递给乐趣!

答案 1 :(得分:4)

被删除的副本是临时返回值到b的副本。如果没有省略,则返回值从a初始化并复制到b。相反,将保留返回值的临时值构造为b并使用a 进行初始化。 [class.copy] / 31:

  

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

如果您在fun中添加其他输出,则可以观察到这一点:

A fun(A a)
{
    cout << "fun!" << endl;
    return a;
}

然后用elision你会得到

  

[...]
  乐趣!
  复制构造函数

没有:

  

[...]
  乐趣!
  复制构造函数
  复制构造函数