Antes se recomienda generalmente las mejores prácticas 1 a pase utilización por ref const de todo tipo , a excepción de orden interna (tipos char
, int
, double
, etc.), para iteradores y para los objetos de función (lambdas, clases derivadas de std::*_function
).
Esto era especialmente cierto antes de la existencia de la semántica de movimiento . La razón es simple: si pasa por valor, debe hacerse una copia del objeto y, a excepción de los objetos muy pequeños, esto siempre es más costoso que pasar una referencia.
Con C ++ 11, hemos ganado semántica de movimiento . En pocas palabras, la semántica de movimiento permite que, en algunos casos, un objeto se pueda pasar "por valor" sin copiarlo. En particular, este es el caso cuando el objeto que está pasando es un valor r .
En sí mismo, mover un objeto sigue siendo al menos tan costoso como pasar por referencia. Sin embargo, en muchos casos, una función copiará internamente un objeto de todos modos, es decir, tomará posesión del argumento. 2
En estas situaciones tenemos la siguiente compensación (simplificada):
- Podemos pasar el objeto por referencia y luego copiarlo internamente.
- Podemos pasar el objeto por valor.
"Pasar por valor" todavía hace que el objeto se copie, a menos que el objeto sea un valor. En el caso de un valor r, el objeto se puede mover en su lugar, de modo que el segundo caso ya no sea "copiar, luego mover" sino "mover, luego (potencialmente) mover de nuevo".
Para los objetos grandes que implementan constructores de movimiento adecuados (como vectores, cadenas ...), el segundo caso es mucho más eficiente que el primero. Por lo tanto, se recomienda usar pasar por valor si la función toma posesión del argumento y si el tipo de objeto admite un movimiento eficiente .
Una nota histórica:
De hecho, cualquier compilador moderno debería poder darse cuenta cuando pasar por valor es costoso, y convertir implícitamente la llamada para usar una constante const si es posible.
En teoria. En la práctica, los compiladores no siempre pueden cambiar esto sin romper la interfaz binaria de la función. En algunos casos especiales (cuando la función está en línea), la copia se eliminará si el compilador puede descubrir que el objeto original no se cambiará a través de las acciones en la función.
Pero, en general, el compilador no puede determinar esto, y el advenimiento de la semántica de movimiento en C ++ ha hecho que esta optimización sea mucho menos relevante.
1 Por ejemplo, en Scott Meyers, C ++ efectivo .
2 Esto es especialmente cierto para los constructores de objetos, que pueden tomar argumentos y almacenarlos internamente para formar parte del estado del objeto construido.