在此 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;
}
这道题我们要找出一个拷贝构造函数被调用了多少次, 根据我的说法,它是 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
中从 - 将
v
分配给f
中的w
- return从
f
- 正在将
f
的 return 值分配给y
u
创建 v
w
因此,对于 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;
}