c++ - retornan - parametros por valor y por referencia
¿Pasar por valor es un incumplimiento razonable en C++ 11? (3)
En C ++ tradicional, pasar por valor en funciones y métodos es lento para objetos grandes, y en general es desaprobado. En cambio, los programadores de C ++ tienden a pasar referencias, lo que es más rápido, pero que introduce todo tipo de preguntas complicadas sobre la propiedad y especialmente sobre la gestión de la memoria (en el caso de que el objeto se asigne en un montón).
Ahora, en C ++ 11, tenemos referencias Rvalue y constructores de movimiento, lo que significa que es posible implementar un objeto grande (como un std::vector
) que es barato para pasar de una función a otra.
Entonces, ¿esto significa que el valor predeterminado debería ser pasar por valor para instancias de tipos como std::vector
y std::string
? ¿Qué pasa con los objetos personalizados? ¿Cuál es la mejor práctica nueva?
En casi todos los casos, su semántica debe ser:
bar(foo f); // want to obtain a copy of f
bar(const foo& f); // want to read f
bar(foo& f); // want to modify f
Todas las demás firmas deben usarse solo con moderación y con buena justificación. El compilador ahora casi siempre lo resolverá de la manera más eficiente. ¡Puedes seguir escribiendo tu código!
Es un defecto razonable si necesita hacer una copia dentro del cuerpo. Esto es lo que defiende Dave Abrahams:
Pauta: no copie los argumentos de su función. En cambio, páselos por valor y deje que el compilador haga la copia.
En código, esto significa que no hagas esto:
void foo(T const& t)
{
auto copy = t;
// ...
}
pero haz esto:
void foo(T t)
{
// ...
}
que tiene la ventaja de que quien llama puede usar foo
así:
T lval;
foo(lval); // copy from lvalue
foo(T {}); // (potential) move from prvalue
foo(std::move(lval)); // (potential) move from xvalue
y solo se hace un trabajo mínimo. Necesitarías dos sobrecargas para hacer lo mismo con las referencias, void foo(T const&);
y void foo(T&&);
.
Con eso en mente, ahora escribí mis valiosos constructores como tales:
class T {
U u;
V v;
public:
T(U u, V v)
: u(std::move(u))
, v(std::move(v))
{}
};
De lo contrario, pasar por referencia a const
todavía es razonable.
Pase los parámetros por valor si dentro del cuerpo de la función necesita una copia del objeto o solo necesita mover el objeto. Pase por const&
si solo necesita acceso no mutante al objeto.
Ejemplo de copia de objeto:
void copy_antipattern(T const& t) { // (Don''t do this.)
auto copy = t;
t.some_mutating_function();
}
void copy_pattern(T t) { // (Do this instead.)
t.some_mutating_function();
}
Ejemplo de movimiento de objetos:
std::vector<T> v;
void move_antipattern(T const& t) {
v.push_back(t);
}
void move_pattern(T t) {
v.push_back(std::move(t));
}
Ejemplo de acceso no mutante:
void read_pattern(T const& t) {
t.some_const_function();
}
Para mayor justificación, vea estas publicaciones de blog de Dave Abrahams y Xiang Fan .