valor referencia por paso parametros funciones entre diferencia c++ pass-by-reference pass-by-value

c++ - parametros - paso por valor y referencia en c



¿Pasar por referencia luego copiar y pasar por valor es funcionalmente diferente? (3)

¿Hay alguna diferencia funcional entre:

void foo(const Bar& bar) { Bar bar_copy(bar); // Do stuff with bar_copy }

y

void foo(Bar bar) { // Do stuff with bar }


Hay algunas diferencias.

void foo(const Bar& bar) { Bar bar_copy(bar); // Do stuff with bar_copy }

no permite evitar la copia, incluso si la bar es temporal y evita también el movimiento de la bar .


Sí, hay diferencias. Mientras que el más obvio es que el tipo de función cambia (y por lo tanto el tipo de su puntero de función), también hay algunas implicaciones menos obvias:

Bar movible construible pero no copiable construible

Por ejemplo, supongamos la siguiente llamada a foo :

foo(Bar());

Para la primera versión, esto se pasará por una referencia a la const bar y luego se copiará utilizando el constructor de copia. Para la segunda versión, el compilador intentará primero mover-constructor.

Esto significa que la única versión de la segunda versión puede ser std::unique_ptr por tipos que solo se pueden mover, como std::unique_ptr . De hecho, la copia forzada manualmente ni siquiera permitirá la compilación de la función .

Obviamente, esto se puede mitigar agregando una ligera complicación:

void foo(Bar&& bar) { // Do something with bar. // As it is an rvalue-reference, you need not copy it. } void foo(Bar const& bar) { Bar bar_copy(bar); foo(std::move(bar_copy)); }

Especificadores de acceso

Curiosamente, hay otra diferencia: el contexto en el que se verifican los permisos de acceso.

Considera la siguiente Bar :

class Bar { Bar(Bar const&) = default; Bar(Bar&&) = default; public: Bar() = default; friend int main(); };

Ahora, la versión de referencia y copia tendrá un error, mientras que la versión de parámetro como valor no se quejará:

void fooA(const Bar& bar) { //Bar bar_copy(bar); // error: ''constexpr Bar::Bar(const Bar&)'' is private } void fooB(Bar bar) { } // OK

Dado que hemos declarado que main es un amigo, se permite la siguiente llamada (tenga en cuenta que el amigo no sería necesario si, por ejemplo, la llamada real se realizó en una función miembro static de Bar ):

int main() { fooB(Bar()); // OK: Main is friend }

Integridad de Bar en el sitio de la llamada

Como se ha observado en los comentarios, si desea que la Bar sea ​​un tipo incompleto en el sitio de la llamada , es posible usar la versión de paso por referencia, ya que esto no requiere que el sitio de la llamada pueda asignar un objeto de tipo Bar .

Copiar efectos secundarios de Elision

C ++ 11 12.8 / 31:

Cuando se cumplen ciertos criterios, una implementación puede omitir la construcción de copiar / mover de un objeto de clase, incluso si el constructor y / o destructor de copia / movimiento del objeto tiene efectos secundarios. En tales casos, la implementación trata el origen y el destino de la operación de copia / movimiento omitida simplemente como dos formas diferentes de referirse al mismo objeto [...]

  • [...]
  • cuando un objeto de clase temporal que no se ha vinculado a una referencia (12.2) se copiaría / movería a un objeto de clase con el mismo tipo no cualificado cv, la operación de copiar / mover se puede omitir construyendo el objeto temporal directamente en el destino de la copia / movimiento omitido
  • [...]

Obviamente, solo la versión de llamada por valor cumple con este criterio: después de pasar por referencia, el parámetro está vinculado a una referencia después de todo. Más allá de una diferencia observable , esto también significa que se pierde una oportunidad de optimización.


Sí, hay una diferencia valiosa.

void foo(Bar bar) puede copiar-construir o mover-construir bar , dependiendo del contexto de llamada.

Y cuando se pasa un temporal a foo(Bar bar) , su compilador puede ser capaz de construir ese temporal directamente donde se espera que esté la bar . Sombrero de punta para niño plantilla.

Su función void foo(const Bar& bar) siempre realiza una copia.

Su función void foo(Bar bar) puede realizar una copia o un movimiento o posiblemente ninguno.