【发布时间】:2016-10-01 06:31:20
【问题描述】:
偶然发现几篇文章声称如果函数无论如何都要进行复制,则按值传递可以提高性能。
我从未真正想过如何在后台实现按值传递。当你这样做时,堆栈上究竟会发生什么:F v = f(g(h()))?
经过一番思考,我得出结论,我会以这样的方式实现它,即 g() 返回的值是在 f() 期望的位置创建的。因此,基本上,没有复制/移动构造函数调用—— f() 将简单地获取 g() 返回的对象的所有权,并在执行离开 f() 的范围时将其销毁。 g() 也一样——它将取得 h() 返回的对象的所有权并在返回时销毁它。
唉,编译器似乎不同意。下面是测试代码:
#include <cstdio>
using std::printf;
struct H
{
H() { printf("H ctor\n"); }
~H() { printf("H dtor\n"); }
H(H const&) {}
// H(H&&) {}
// H(H const&) = default;
// H(H&&) = default;
};
H h() { return H(); }
struct G
{
G() { printf("G ctor\n"); }
~G() { printf("G dtor\n"); }
G(G const&) {}
// G(G&&) {}
// G(G const&) = default;
// G(G&&) = default;
};
G g(H) { return G(); }
struct F
{
F() { printf("F ctor\n"); }
~F() { printf("F dtor\n"); }
};
F f(G) { return F(); }
int main()
{
F v = f(g(h()));
return 0;
}
在 MSVC 2015 上,它的输出正是我所期望的:
H ctor
G ctor
H dtor
F ctor
G dtor
F dtor
但是如果你注释掉复制构造函数,它看起来像这样:
H ctor
G ctor
H dtor
F ctor
G dtor
G dtor
H dtor
F dtor
我怀疑删除用户提供的复制构造函数会导致编译器生成移动构造函数,这反过来会导致不必要的“移动”,无论有多大的对象都不会消失(尝试添加 1MB 数组作为成员变量)。 IE。编译器非常喜欢“移动”,以至于它选择它而不是根本不做任何事情。
这似乎是 MSVC 中的一个错误,但我真的希望有人解释(和/或证明)这里发生了什么。这是问题 #1。
现在,如果您尝试 GCC 5.4.0 输出根本没有任何意义:
H ctor
G ctor
F ctor
G dtor
H dtor
F dtor
必须在创建 F 之前销毁 H! H 是 g() 范围的局部变量!请注意,在这里使用构造函数对 GCC 的影响为零。
与 MSVC 相同——对我来说似乎是一个错误,但有人可以解释/证明这里发生了什么吗?那是问题 #2。
在专业使用 C++ 多年后,我遇到了这样的问题,这真是太愚蠢了……在将近 4 年之后,编译器仍然无法就如何传递值达成一致?
【问题讨论】:
-
以防万一——我知道 RVO 是什么……而且我非常了解 C++。然而我找不到这两个问题的好答案
-
您使用什么优化级别? “编译器不能同意”,复制构造器省略是一个优化 - 所以它是否发生是一个QOI问题(你的程序的正确性不能依赖它)。
-
优化 lvls 无效。在标准中找不到任何关于 cctor/mctor 省略的东西来解释这里发生了什么。请注意,我们正在讨论将正确类型的 rvalues 作为参数传递给另一个函数——我有点希望这里没有任何额外的副本(或移动)......
-
这个链接可以帮助你弄清楚发生了什么:en.cppreference.com/w/cpp/language/copy_elisionGood lucky
标签: c++ gcc visual-c++