在此 C++ 代码段中,复制构造函数被调用了多少次?

How many times is the copy constructor is called in this C++ snippet?

这道题我们要找出一个拷贝构造函数被调用了多少次, 根据我的说法,它是 5,但答案是 7。这是怎么回事?

Widget  f(Widget  u)
{
    Widget v(u);
    Widget w = v;
    return w;
}
int main()
{
    Widget x;
    Widget y = f(f(x));
}

禁用复制省略和移动有:

1) Widget y = f(f(x));

f 函数中有 4 次复制构造函数调用。

1) u按值传递。

2) v 是从 u.

复制初始化的

3) w 是从 v.

复制初始化的

4) w 被复制到 return.

所以,实际上有9次调用。

在启用复制省略的情况下,gcc/clang 上有 5 个调用。

+1 - 赋值 Widget y = f(f(x))

+2 - 调用函数 f() 两次,按值而不是按引用传递参数。

+2 - 复制 v(u) 调用了两次

+2 - 复制初始化 Widget w = v 调用了两次

7 个。

请注意,在某些设置下,某些编译器确实如此。其他答案也是正确的,我解释了为什么结果可能是 7,例如,您可以在 bcc32.

下获得

how many times a copy-constructor is called, according to me its 5 but answer is 7. how is that happening?

将您的代码片段更新为实际有效的代码后(见底部),您可以看到以下输出(test.cpp 中的代码):

➜  /tmp g++ -o test -std=c++11 test.cpp && ./test
default ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor

我们只需要看看 单个 函数调用是如何工作的。为此,我们有:

Widget  f(Widget  u)
{
        Widget v(u);
        Widget w = v;
        return w;
}

y = f(x)

复制构造函数在以下情况下被调用:

  • x 发送到 f,按值
  • f
  • 中从 u 创建 v
  • v 分配给 f 中的 w
  • return从f
  • w
  • 正在将 f 的 return 值分配给 y

因此,对于 f 的每次调用,我们需要使用复制构造函数 4 次,并且由于 f 被调用两次,您已经拥有 +8 复制构造函数为 y 中的最终作业调用 +1,总计 9

但是为什么我们在上面的输出中只看到 5?

答案是:优化

GCC 正在通过优化删除一些复制操作。如果我们使用 -fno-elide-constructors 标志构建它,我们可以看到所有这些:

➜  /tmp  g++ -fno-elide-constructors -o -std=c++11 test test.cpp && ./test
def ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor
copy ctor

这告诉 GCC 执行我们之前构建中看到的优化,它显示了用于 x 的默认构造函数下方的所有复制构造函数调用。

使用的代码片段

#include <iostream>
using namespace std;

class Widget {

public:
        Widget() { cout << "def ctor" << endl; }
        Widget(const Widget &other) { cout << "copy ctor" << endl; }
};

Widget  f(Widget u)
{
        Widget v(u);
        Widget w = v;
        return w;
}

int main()
{
        Widget x;
        Widget y = f(f(x));
        return 0;
}