He estado aprendiendo acerca de los constructores de movimientos durante el último día, tratando de cumplir con una regla general de devolver por valor como la mayoría de la gente parece sugerir, y he encontrado una interesante (yo) dilema.RVO, mover operaciones y un dilema
Supongamos que tengo un costoso para construir/copiar la clase 'C' que ha definido correctamente el constructor de copia, el operador de asignación, el constructor de movimientos y el operador de asignación de movimiento.
primer lugar, esta pieza de código elude el constructor de copia como esperaba:
C make_c1() {
return C();
}
como lo hace esto:
C make_c2() {
C tmp;
return tmp;
}
y lo mismo ocurre esto (si yo paso en un 1 o 2) :
C make_c3(int a) {
return a == 1 ? make_c1() : make_c2();
}
es cuando me llegar a este que tengo un problema:
C make_c4(int a) {
C tmp;
return a == 1 ? make_c1() : tmp;
}
Al pasar en 1 se dispara RVO para el resultado de make_c1, pero al pasar en 2 se activa el constructor de copias en tmp.
de Modificación de la función a la siguiente provoca el movimiento constructor para ser activado para tmp en su lugar:
C make_c5(int a) {
C tmp;
return a == 1 ? make_c1() : std::move(tmp);
}
Todos grande y maravilloso, excepto ...
En estos ejemplos simples, RVO se ha disparado bastante tanto como esperaba.
Sin embargo, ¿qué pasa si mi código es un poco más complejo y en algunos compiladores no evoca RVO en esa última función? En ese caso, tendría que ajustar mi llamada a make_c1 en std :: move, lo que hará que el código sea menos eficiente en los compiladores que evocan a RVO.
Así que mis preguntas son:
- ¿Por qué el movimiento constructor no invoca en make_c4 cuando regresé mi objeto local? (Está a punto de ser destruido después de todo).
- En la función make_c5, ¿debo devolver los resultados de make_c1 por valor o moverlos? (Para evitar diferentes versiones del código para diferentes compiladores/plataformas).
- ¿Existe alguna forma mejor de codificar la función final para que haga lo correcto para una implementación razonable del compilador?
El compilador con el que he estado jugando es GCC 4.5.3 en Cygwin.
No estoy seguro de que esto responda todas mis preguntas, aunque sí "corrige" el código específico con el que estoy jugando. ¿Por qué funciona tu forma de 2 declaraciones de devolución mientras que mi formulario no? ¿Es esto un problema de calidad del compilador? Supongo que parte de mi problema es que RVO depende completamente del compilador, mientras que el movimiento está bajo mi control. –
@ IanM_Matrix1: no debe confiar en una optimización particular para la ganancia crítica de rendimiento, porque es una ciencia misteriosa y no puede predecir con fiabilidad cuándo se activará o no. Esto es lo que nos trae 'mover': la capacidad de controlar, de cierta manera, lo que está sucediendo. Si su clase es cara de copiar, pensaría en deshabilitar la copia para evitar el uso accidental. –
Estoy totalmente de acuerdo con eso, pero a veces es necesario copiar ** y ** caro, aunque una función de tipo clon le daría un mejor control sobre eso. De todos modos, parece que mis preguntas han sido respondidas con la última actualización de Howard: 1 es respondida en la última actualización, 2 es respondida por 'do not' y 3 es respondida por su código original. Gracias Howard :) –