valor tag sucursales sacar puede persona para pagar otra obtener nombre maipu donde domicilio cómo c++ return-value-optimization copy-elision rvo

c++ - tag - ¿Por qué no se permite RVO al devolver un parámetro?



sucursales tag (2)

Imagina que no_rvo se define en un archivo diferente al main modo que al compilar main el compilador solo verá la declaración

X no_rvo(X x);

y no tendrá idea de si el objeto del tipo X devuelto tiene alguna relación con el argumento. Por lo que se sabe en ese momento, la implementación de no_rvo podría ser

X no_rvo(X x) { X other; return other; }

Así que cuando por ejemplo compila la línea

X const& x = no_rvo(X());

Hará lo siguiente, cuando optimice al máximo.

  • Genera la X temporal que se no_rvo a no_rvo como argumento
  • llama a no_rvo y vincula su valor de retorno a x
  • no_rvo el objeto temporal que pasó a no_rvo .

Ahora, si el valor de retorno de no_rvo sería el mismo objeto que el objeto pasado a él, la destrucción del objeto temporal significaría la destrucción del objeto devuelto. Pero eso sería incorrecto porque el objeto devuelto está vinculado a una referencia, por lo tanto, extiende su vida útil más allá de esa declaración. Sin embargo, simplemente no destruir el argumento tampoco es una solución porque eso sería incorrecto si la definición de no_rvo es la implementación alternativa que he mostrado anteriormente. Por lo tanto, si la función puede reutilizar un argumento como valor de retorno, pueden surgir situaciones en las que el compilador no pudo determinar el comportamiento correcto.

Tenga en cuenta que con las implementaciones comunes, el compilador no podría optimizar eso de todos modos, por lo tanto, no es una pérdida tan grande que no se permita formalmente. También tenga en cuenta que al compilador se le permite optimizar la copia de todos modos si puede probar que esto no conduce a un cambio en el comportamiento observable (la llamada regla "como si").

Se indica en [C ++ 11: 12.8 / 31]:

Esta elision de operaciones de copia / movimiento, llamada elision de copia, esta permitida [...]:

- en una declaración de retorno en una función con un tipo de retorno de clase, cuando la expresión es el nombre de un objeto automático no volátil (que no sea un parámetro de función o cláusula catch ) con el mismo tipo no cualificado cv que el tipo de retorno de función , la operación de copiar / mover se puede omitir construyendo el objeto automático directamente en el valor de retorno de la función

Esto implica

#include <iostream> using namespace std; struct X { X() { } X(const X& other) { cout << "X(const X& other)" << endl; } }; X no_rvo(X x) { cout << "no_rvo" << endl; return x; } int main() { X x_orig; X x_copy = no_rvo(x_orig); return 0; }

imprimirá

X(const X& other) no_rvo X(const X& other)

¿Por qué se requiere el segundo constructor de copia? ¿No puede un compilador simplemente extender la vida útil de x ?


La implementación habitual de RVO es que el código de llamada pasa la dirección de un fragmento de memoria donde la función debe construir su objeto de resultado.

Cuando el resultado de la función es directamente una variable automática que no es un argumento formal, esa variable local se puede colocar simplemente en el fragmento de memoria provisto por el llamante, y la declaración de retorno no realiza ninguna copia.

Para un argumento pasado por valor, el código de la máquina que llama tiene que copiar-inicializar su argumento real en la ubicación del argumento formal antes de saltar a la función. Para que la función coloque su resultado allí, tendría que destruir primero el objeto de argumento formal, que tiene algunos casos especiales difíciles (por ejemplo, cuando esa construcción se refiere directa o indirectamente al objeto de argumento formal). Por lo tanto, en lugar de identificar la ubicación del resultado con la ubicación del argumento formal, una optimización aquí lógicamente tiene que usar una porción de memoria proporcionada por separado para el resultado de la función.

Sin embargo, la persona que llama normalmente proporciona un resultado de función que no se pasa en un registro. Es decir, de lo que uno podría hablar razonablemente como RVO, una especie de RVO disminuido, para el caso de una expresión de return que denota un argumento formal, es lo que sucedería de todos modos. Y no encaja con el texto "al construir el objeto automático directamente en el valor de retorno de la función".

En resumen, el flujo de datos que requiere que la persona que llama pase un valor, significa que es necesariamente la persona que llama que inicializa el almacenamiento de un argumento formal, y no la función. Por lo tanto, la copia de un argumento formal no se puede evitar en general (ese término de comadreja cubre los casos especiales en los que el compilador puede hacer cosas muy especiales, en particular para el código de máquina en línea). Sin embargo, es la función que inicializa el almacenamiento de cualquier otro objeto automático local, y luego no es un problema hacer RVO.