RVO(Return Value Optimization)是否保证或适用于C ++编译器(特别是GCC)中的所有对象和情境?
如果答案是“否”,那么这个类/对象的优化条件是什么?如何强制或鼓励编译器对特定的返回值执行RVO?
答案 0 :(得分:41)
返回值优化可以始终应用,不能普遍应用的是命名返回值优化。基本上,为了进行优化,编译器必须知道将在构造对象的位置返回哪个对象。
在RVO(返回临时)的情况下,该条件得到满足:对象在return语句中构造,并且返回它。
对于NRVO,您必须分析代码以了解编译器是否可以知道该信息。如果对函数的分析很简单,那么编译器可能会优化它(单个return语句不包含条件,例如;同一对象的多个return语句;多个return语句,如T f() { if (condition) { T r; return r; } else { T r2; return r2; } }
其中编译器知道将返回r
或r2
...)
请注意,您只能在简单的情况下假设优化,具体而言,维基百科中的示例实际上可以通过足够聪明的编译器进行优化:
std::string f( bool x ) {
std::string a("a"), b("b");
if ( x ) return a;
else return b;
}
可以由编译器重写为:
std::string f( bool x ) {
if ( x ) {
std::string a("a"), b("b");
return a;
} else {
std::string a("a"), b("b");
return b;
}
}
此时编译器可以知道在第一个分支a
中将构造代替返回的对象,而在第二个分支中,同样适用于b
。但我不指望这一点。如果代码很复杂,则假设编译器无法生成优化。
编辑:有一个案例我没有明确提到,不允许编译器(在大多数情况下,即使允许编译器,也不可能这样做)来优化掉副本从函数的参数到return语句:
T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
// a move operation if available.
答案 1 :(得分:5)
是否保证gcc编译器中所有对象的RVO(返回值优化)?
无法保证优化(尽管RVO相当可靠,但确实存在some cases that throw it off)。
如果答案为“否”,那么对于类/对象进行此优化的条件是什么?
一个非常故意从你身上抽象出来的实现细节。
请不要知道也不关心这个。
答案 2 :(得分:3)
对于Jesper:如果要构建的对象很大,则可能需要避免复制(或者至少非常需要)。
如果发生RVO,则避免复制,您无需再编写任何代码行。
如果没有,你必须手动完成,自己编写额外的脚手架。这可能涉及提前指定一个缓冲区,迫使你为这个空(可能无效,你可以看到这个不干净)对象和一个“构造”这个无效对象的方法写一个构造函数。
所以'如果有保证,它可以减少我的代码行。不是吗?'并不意味着马苏德是个白痴。不幸的是,RVO并不能保证。你必须测试它是否发生,如果没有,写下脚手架并污染你的设计。它不能被束缚。
答案 3 :(得分:2)
R值参考(C ++ 11的新功能)是您的问题的解决方案,允许您使用Type(Type &&r);
(移动构造函数)明确地,而不是Type(const Type &r)
(复制构造函数)。
例如:
class String {
public:
char *buffer;
String(const char *s) {
int n = strlen(s) + 1;
buffer = new char[n];
memcpy(buffer, s, n);
}
~String() { delete [] buffer; }
String(const String &r) {
// traditional copy ...
}
String(String &&r) {
buffer = r.buffer; // O(1), No copying, saves time.
r.buffer = 0;
}
};
String hello(bool world) {
if (world) {
return std::move( String("Hello, world.") );
} else {
return std::move( String("Hello.") );
}
}
int main() {
String foo = hello();
std::cout <<foo.buffer <<std::endl;
}
这不会触发复制构造函数。
<强> EDIT1 强>
代码
return std::move( String(...) );
可以缩短为
return String(...);
由于临时对象默认为R-value-ref。
答案 4 :(得分:0)
我没有是或否答案,但你说如果你正在寻找的优化得到保证,你可以编写更少的代码行。
如果您编写需要编写的代码,程序将始终有效,如果有优化,它将更快地运行。如果确实存在优化“填补空白”的逻辑而不是代码的机制并使其工作,或彻底改变逻辑的情况,这似乎是我想要修复的错误而不是我希望依赖或利用的实现细节。