Parece haber cierta confusión sobre cómo funciona el RVO (Return Value Optimization).
Un ejemplo sencillo:
#include <iostream>
struct A {
int a;
int b;
int c;
int d;
};
A create(int i) {
A a = {i, i+1, i+2, i+3 };
std::cout << &a << "\n";
return a;
}
int main(int argc, char*[]) {
A a = create(argc);
std::cout << &a << "\n";
}
y su salida al ideone:
0xbf928684
0xbf928684
Sorprendente?
realidad, que es el efecto de RVO: el objeto a ser devuelto se construye directamente en lugar en la persona que llama.
¿Cómo?
Tradicionalmente, la persona que llama (main
aquí) se reserva espacio en la pila para obtener el valor de retorno: la ranura de retorno ; el destinatario (create
aquí) pasa (de alguna manera) la dirección de la ranura de devolución para copiar su valor de retorno. A continuación, el destinatario asigna su propio espacio para la variable local en la que genera el resultado, como para cualquier otra variable local, y luego lo copia en el espacio de retorno en la instrucción return
.
RVO se activa cuando el compilador deduce del código que la variable se puede construir directamente en el ranura de retorno con semántica equivalente (la regla de si-si).
Tenga en cuenta que esta es una optimización tan común que está explícitamente incluida en la lista blanca por el estándar y el compilador no tiene que preocuparse por los posibles efectos secundarios del constructor de copia (o movimiento).
¿Cuándo?
El compilador es más probable que utilice reglas simples, tales como:
// 1. works
A unnamed() { return {1, 2, 3, 4}; }
// 2. works
A unique_named() {
A a = {1, 2, 3, 4};
return a;
}
// 3. works
A mixed_unnamed_named(bool b) {
if (b) { return {1, 2, 3, 4}; }
A a = {1, 2, 3, 4};
return a;
}
// 4. does not work
A mixed_named_unnamed(bool b) {
A a = {1, 2, 3, 4};
if (b) { return {4, 3, 2, 1}; }
return a;
}
En este último caso (4), la optimización no se puede aplicar cuando A
se devuelve porque el compilador no puede construir a
en el ranura de devolución, ya que puede necesitarlo para otra cosa (dependiendo de la condición booleana b
).
Una simple regla de oro es de esta manera que:
RVO debe aplicarse si no hay otro candidato a la ranura de retorno ha sido declarado con anterioridad a la declaración return
.
semántica de movimiento: http://www2.research.att.com/~bs/C++0xFAQ.html#rval – chris
No necesariamente creará una copia. NRVO o movimientos simbólicos pueden prevenir eso. –
Puede confiar en su compilador para realizar magia de NRVO o usar explícitamente la semántica de movimiento. –