valores tipos retorno referencias referencia programacion por paso parametros funciones ejemplos con c++ optimization return-value-optimization temporary-objects const-reference

c++ - tipos - Referencia constante a la optimización del valor de retorno temporal



valores de retorno en c (2)

Consideremos el caso más simple:

lofactory( ... ).some_method();

En este caso, es posible realizar una copia de la lofactía al contexto de la persona que llama, pero RVO / NRVO puede optimizarla.

LargeObject mylo2 ( lofactory( ... ) );

En este caso las posibles copias son:

  1. RVO / NRVO puede optimizar la devolución de forma temporal desde el contexto de la empresa a la persona que llama.
  2. Copiar-construir mylo2 desde temporal - puede ser optimizado por copia-elision

const LargeObject& mylo1 = lofactory( ... );

En este caso, todavía es posible una copia:

  1. Regreso temporal de la lofactory al contexto de la persona que llama - puede ser optimizado por RVO / NRVO (¡también!)

Una referencia se unirá a este temporal.

Asi que,

¿Existen reglas generalmente aceptadas o mejores prácticas sobre cuándo usar const y to temporaries y cuándo confiar en RVO / NRVO?

Como dije anteriormente, incluso en un caso con const& , es posible una copia innecesaria, y RVO / NRVO puede optimizarla.

Si su compilador aplica RVO / NVRO en algún caso, entonces lo más probable es que se haga una copia de la elección en la etapa 2 (arriba). Porque en ese caso, copy-elision es mucho más simple que NRVO.

Pero, en el peor de los casos, tendrá una copia para const& case, y dos copias cuando inicie el valor.

¿Podría haber una situación en la que usar el método const y es peor que no usarlo?

No creo que haya tales casos. Al menos a menos que su compilador use reglas extrañas que discriminen const& . (Para un ejemplo de una situación similar, noté que MSVC no hace NVRO para la inicialización agregada).

(Estoy pensando, por ejemplo, en C ++ 11 mover la semántica si LargeObject tiene implementados ...)

En C ++ 11, si LargeObject tiene semántica de movimiento, en el peor de los casos, tendrá un movimiento para la const& caso, y dos movimientos cuando inicie el valor. Por lo tanto, const& sigue siendo un poco mejor.

Entonces, ¿una buena regla sería siempre vincular temporarios a constantes y, si es posible, ya que podría evitar una copia si el compilador no hace una copia de la elección por alguna razón?

Sin saber el contexto real de la aplicación, esto parece una buena regla.

En C ++ 11 es posible vincular temporalmente a la referencia de valor - LargeObject &&. Por lo tanto, tal temporal puede ser modificado.

Por cierto, la emulación semántica de movimiento está disponible para C ++ 98/03 por diferentes trucos. Por ejemplo:

Sin embargo, incluso en presencia de movimiento semántico, hay objetos que no se pueden mover de forma barata. Por ejemplo, 4x4 clase matricial con datos dobles [4] [4] dentro. Por lo tanto, Copy-elision RVO / NRVO aún son muy importantes, incluso en C ++ 11. Y, por cierto, cuando ocurre Copy-elision / RVO / NRVO, es más rápido que mover.

PD: en casos reales, hay algunas cosas adicionales que deben ser consideradas:

Por ejemplo, si tiene una función que devuelve vector, incluso si se aplicara Move / RVO / NRVO / Copy-Elision, es posible que aún no sea 100% eficiente. Por ejemplo, considere el siguiente caso:

while(/*...*/) { vector<some> v = produce_next(/* ... */); // Move/RVO/NRVO are applied // ... }

Será más eficiente cambiar el código a:

vector<some> v; while(/*...*/) { v.clear(); produce_next( v ); // fill v // or something like: produce_next( back_inserter(v) ); // ... }

Porque en este caso, la memoria ya asignada dentro del vector se puede reutilizar cuando v.capacity () es suficiente, sin necesidad de hacer nuevas asignaciones dentro de produce_next en cada iteración.

Soy consciente del hecho de que la asignación de un valor a una referencia de valor constante prolonga la vida útil temporal hasta el final del alcance. Sin embargo, no me queda claro cuándo usar esto y cuándo confiar en la optimización del valor de retorno.

LargeObject lofactory( ... ) { // construct a LargeObject in a way that is OK for RVO/NRVO } int main() { const LargeObject& mylo1 = lofactory( ... ); // using const& LargeObject mylo2 = lofactory( ... ); // same as above because of RVO/NRVO ? }

De acuerdo con el C ++ más efectivo de Scot Meyers (artículo 20), el compilador podría optimizar el segundo método para construir el objeto en su lugar (lo que sería ideal y exactamente lo que se intenta lograr con el método const& en el primer método).

  1. ¿Existen reglas generalmente aceptadas o mejores prácticas sobre cuándo usar const& to temporaries y cuándo confiar en RVO / NRVO?
  2. ¿Podría haber una situación en la que usar el método const& es peor que no usarlo? (Estoy pensando, por ejemplo, en C ++ 11 mover la semántica si LargeObject tiene implementados ...)

Si escribes tu clase de lofactory así:

LargeObject lofactory( ... ) { // figure out constructor arguments to build a large object return { arg1, arg2, arg3 } // return statement with a braced-init-list }

En este caso no hay RVO / NRVO, es construcción directa. La sección 6.6.3 de la norma dice: "Una declaración de return con una lista-inic de paréntesis inicializa el objeto o la referencia que se devolverá desde la función mediante copia-lista-inicialización (8.5.4) desde la lista de inicialización especificada".

Entonces, si captura su objeto con

LargeObject&& mylo = lofactory( ... );

no habrá ninguna copia, el tiempo de vida será lo que usted esperaría, y puede modificar mylo.

Y todo sin copia en ningún lado, garantizado.