2009-09-08 18 views
23

Considere las tres funciones.Comprensión de la optimización del valor de retorno y el retorno de los temporales - C++

std::string get_a_string() 
{ 
    return "hello"; 
} 

std::string get_a_string1() 
{ 
    return std::string("hello"); 
} 

std::string get_a_string2() 
{ 
    std::string str("hello"); 
    return str; 
} 
  1. RVO se aplicará en todos los tres casos?
  2. ¿Está bien devolver un código temporal como en el código anterior? Creo que está bien, ya que lo devuelvo por valor en lugar de devolver cualquier referencia al mismo.

¿Alguna idea?

Respuesta

16

En dos primeros casos se llevará a cabo la optimización RVO. RVO es una característica antigua y la mayoría de los compiladores lo admiten. El último caso se llama NRVO (RVO con nombre). Esa es una característica relativamente nueva de C++. El estándar permite, pero no requiere la implementación de NRVO (así como RVO), pero algunos compiladores lo soportan.

Puede leer más sobre RVO en el ítem 20 del libro de Scott Meyers More Effective C++. 35 New Ways to Improve Your Programs and Designs.

Here es un buen artículo sobre NRVO en Visual C++ 2005.

+0

¿Hay alguna forma en VS2008 de desactivar todas las optimizaciones, incluido RVO? He apagado todo, pero todavía está haciendo RVO. Esto es solo para entender las diferencias. –

+0

'/ Od' debe deshabilitar NRVO, pero no estoy seguro acerca de RVO. –

+0

Gracias. Déjame probar eso –

7

En primer lugar, está bien devolver un valor temporal que es lo que usted hace. Se copia y, aunque el original saldrá del alcance, la copia no lo hará y la persona que llama puede usarlo con seguridad.

En segundo lugar, los tres casos son idénticos (ya que de todos modos no se accede al temporal en el tercer caso) y un compilador incluso podría emitir el mismo código para todos ellos. Por lo tanto, puede usar RVO en los tres casos. Esto es completamente dependiente del compilador.

+0

Gracias por su respuesta. –

1
  1. Depende de su compilador, ¿a qué plataforma se refiere? La mejor forma de averiguarlo es compilar una aplicación de prueba pequeña muy y verificar el ASM que produce su compilador.

  2. Sí, está bien, aunque nunca mencione lo que le preocupa; ¿velocidad? ¿estilo? puede hacer una referencia temporal local a una const - la vida útil del temporal se extenderá a la duración de la referencia - ¡pruébelo y compruébelo usted mismo! (Herb Sutter explica esto here) Consulte el final de la publicación, por ejemplo.

IMO casi siempre es mejor confiar en su compilador para optimizar su código para usted. Hay muy pocos casos en los que deba preocuparse por este tipo de cosas (el código de muy bajo nivel es uno de esos ámbitos, donde es interactivo con los registros de hardware).

int foo() { return 42; } 

int main(int, char**) 
{ 
    const int &iRef = foo(); 
    // iRef is valid at this point too! 
} 
+4

"puede devolver una referencia constante a un local temporal" no, eso está mal. No puede devolver una referencia a un local ni a los temporales creados en esa función. Serán destruidos una vez que la función regrese, y el valor de retorno de referencia se referirá a la basura. Creo que tenías 'const T & t = T();' en mente, que es una cuestión diferente. –

+0

Sí, eso es lo que tenía en mente. Lo he hecho más explícito ahora. – Thomi

2

Todos los casos son correctos. Todos construirán un temporal y aplicarán el constructor de copia del tipo de devolución. Necesariamente, si no hay un constructor de copia, el código fallará.

RVO sucederá en los tres casos en la mayoría de los compiladores. La única diferencia es la última donde el estándar no lo fuerza. Esto porque tienes una variable con nombre. Pero la mayoría de los compiladores son lo suficientemente inteligentes como para aplicar RVO a él ... cuanto más tarde se declare la variable nombrada y menos transformaciones se aplique, mejores serán las probabilidades de que se aplique RVO a una variable con nombre.

Dicho sea de paso, es posible devolver una referencia como posiblemente haya visto en otro código. Lo que no debes hacer es devolver una referencia a un objeto local.

std::string& get_a_string2() 
{ 
    std::string str("hello"); 
    return str; //error! 
} 

Producirá un error de tiempo de compilación, como usted sabe. Sin embargo,

std::string& get_a_string2(std::string& str) 
{ 
    // do something to str 
    return str; //OK 
} 

Funcionará bien. En este caso, no hay construcción de construcción o copia involucrada. Simplemente la función devuelve una referencia a su argumento.

+1

Sin embargo, preferiría tener esa función como vacía y no devolver nada a medida que procesa el parámetro. Devolver el parámetro de referencia simplemente confundiría a otras personas al mirar el código. –

+1

sí. Fue solo un ejemplo ilustrativo. –

Cuestiones relacionadas