9

考虑以下代码:

#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 是如何创建的(因为在这种情况下也没有构造函数调用)?

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

4

2 回答 2

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的参数),然后将中间体直接复制到b中,跳过典型的将 a 复制到返回中间体的过程。如果您按值传递(更改为A fun(const A& a)

Constructing: 0x7fff8e9642b0
Constructing: 0x7fff8e9642a0
Copy Constructor: 0x7fff8e964290 from 0x7fff8e9642a0
a:0x7fff8e9642b0
b:0x7fff8e964290
c:0x7fff8e9642a0

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

于 2014-11-14T19:27:41.407 回答
4

被省略的副本是临时返回值的副本b。没有省略,返回值被初始化a并复制到b. 取而代之的是,将保存返回值的临时变量构造为b并初始化为a. [class.copy]/31:

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

如果您在以下位置添加额外的输出,您可以观察到这一点fun

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

然后用省略你会得到

[…]
有趣!
复制构造函数

并且没有:

[…]
有趣!
复制构造函数
复制构造函数

于 2014-11-14T19:26:23.863 回答