我试图解释为什么一个相当好的C++ 11编译器(clang)没有优化这段代码,并想知道这里是否有人有自己的观点。
#include <iostream>
#define SLOW
struct A {
A() {}
~A() { std::cout << "A d'tor\n"; }
A(const A&) { std::cout << "A copy\n"; }
A(A&&) { std::cout << "A move\n"; }
A &operator =(A) { std::cout << "A copy assignment\n"; return *this; }
};
struct B {
// Using move on a sink.
// Nice talk at Going Native 2013 by Sean Parent.
B(A foo) : a_(std::move(foo)) {}
A a_;
};
A MakeA() {
return A();
}
B MakeB() {
// The key bits are in here
#ifdef SLOW
A a(MakeA());
return B(a);
#else
return B(MakeA());
#endif
}
int main() {
std::cout << "Hello World!\n";
B obj = MakeB();
std::cout << &obj << "\n";
return 0;
}如果我使用#define SLOW进行注释并使用-s进行优化,那么
Hello World!
A move
A d'tor
0x7fff5fbff9f0
A d'tor这是意料之中的。
如果我在启用#define SLOW并使用-s进行优化的情况下运行该程序,则会得到:
Hello World!
A copy
A move
A d'tor
A d'tor
0x7fff5fbff9e8
A d'tor这显然不太好。所以问题是:
为什么我没有看到NRVO优化应用在“慢”的情况下?我知道编译器不需要应用NRVO,但这似乎是一个常见的简单情况。
一般来说,我试图鼓励使用“缓慢”样式的代码,因为我发现调试起来容易得多。
发布于 2013-12-18 05:17:19
简单的答案是:因为在这种情况下不允许应用复制省略。编译器只允许在极少数和特定情况下应用复制省略。引用的标准是12.8 class.copy第31段:
..。这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以合并以消除多个副本):
显然,B(a)的类型不是A,即不允许复制省略。同一段中的其他符号引用诸如throw表达式、从临时声明和异常声明中选择副本等内容。这些都不适用。
发布于 2014-01-05 23:45:23
在慢速路径中看到的复制不是由于缺少RVO,而是因为在B(MakeA())中,"MakeA()“是一个rvalue,而在B(a)中,"a”是一个lvalue。
为了澄清这一点,让我们修改慢速路径,以指示MakeA()在何处完成:
#ifdef SLOW
A a(MakeA());
std::cout << "---- after call \n";
return B(a);
#else产出如下:
Hello World!
---- after call
A copy
A move
A d'tor
A d'tor
0x7fff5a831b28
A d'tor,这表明没有在
A a(MakeA());因此,RVO确实发生了。
删除所有副本的修复程序是:
return B(std::move(a));https://stackoverflow.com/questions/20649951
复制相似问题