RVO(返回值优化)在C++编译器中是否对所有对象和情况都有保证或适用(特别是GCC)?
如果答案是否定的,那么这种优化对类/对象的条件是什么?如何强制或鼓励编译器对特定的返回值进行RVO?
RVO(返回值优化)在C++编译器中是否对所有对象和情况都有保证或适用(特别是GCC)?
如果答案是否定的,那么这种优化对类/对象的条件是什么?如何强制或鼓励编译器对特定的返回值进行RVO?
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
。但我不会指望它。如果代码比较复杂,就假设编译器不能产生优化。
编辑:还有一种情况我没有明确提到,编译器不允许(在大多数情况下即使允许,也不可能这样做)从函数参数到返回语句中的拷贝进行优化:
T f( T value ) { return value; } // Cannot be optimized away --but can be converted into
// a move operation if available.
在gcc编译器中,是否保证所有对象都能使用RVO(返回值优化)?
没有任何优化是保证的(尽管RVO相当可靠,但确实存在某些情况会影响它)。
如果答案是“否”,那么类/对象进行此优化的条件是什么?
这是一个故意从您抽象出来的实现细节。
请不要关心或了解此问题。
移动语义(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 String("Hello, world.");
} else {
return String("Hello.");
}
}
int main() {
String foo = hello();
std::cout <<foo.buffer <<std::endl;
}
这不会触发复制构���函数。
return
一个对象将使其构造一个新对象(通过复制或移动),而不是临时对象的引用。只有当返回类型为引用或指针时,你所说的才会发生。 - RnMssstd::move
。这样做没有任何好处,实际上可能会阻止编译器利用RVO。具有讽刺意味的是,这回答了“是否适用于所有对象的RVO?”的问题,因为它产生了一个不允许使用RVO的情况。 - Brian61354270T foo(T&& x) { return x; }
这将执行复制构造而不是移动构造。为了避免复制构造,你必须写成 return std::move(x);
。所以,如果你不想要一份拷贝但不确定是否有拷贝省略,就使用move,它不会有任何问题。 - RnMss给Jesper:如果要构建的对象很大,避免复制可能是必要的(或者至少非常可取)。
如果发生RVO,则避免了复制,您无需再编写任何代码。
如果没有发生RVO,则您将不得不手动执行此操作,自己编写额外的脚手架。这可能涉及事先指定缓冲区,强制您为此空(可能无效,您可以看到这不干净)对象编写构造函数和“构造”此无效对象的方法。
因此,“如果保证可以减少我的代码行数。这不是吗?”并不意味着Masoud是个白痴。但不幸的是,RVO不能保证。您必须测试它是否发生,如果没有,编写脚手架并污染设计。这是无法避免的。
我没有一个肯定或否定的答案,但你说如果你要寻找的优化是有保证的话,你可以写更少的代码。
如果你写了你需要写的代码,程序总是会工作的,如果优化存在,它会更快地工作。如果确实存在这样一种情况,即优化“填补”逻辑而不是代码机制并使其工作,或者直接改变逻辑,那么这似乎是一个错误,我希望修复它,而不是依赖或利用实现细节。
value
不是通过传值方式临时生成的吗?因此完全符合RVO的条件,对吗? - Owenvalue
不会超出函数的生命周期,但它也不是临时的(虽然这并不重要)。该语言没有为省略该复制提供规定,这就是注释的原因。至于为什么,复制省略是通过直接在复制将要占用的内存上创建复制来完成的,然后就不需要复制了。常见的ABIs让调用者保留返回对象的空间,并且调用者也会复制参数。但在一个具有分离编译的语言中,调用者不知道函数是否会返回该参数[...] - David Rodríguez - dribeas