Cuando se sobrecarga operator=
, que puede escríbalo para devolver el tipo que desee. Si lo desea lo suficiente, puede sobrecargar X::operator=
para devolver (por ejemplo) una instancia de alguna clase completamente diferente Y
o Z
. Esto generalmente es altamente desaconsejable.
En particular, generalmente desea admitir el encadenamiento de operator=
como C lo hace. Por ejemplo:
int x, y, z;
x = y = z = 0;
Siendo ese el caso, normalmente se desea devolver un valor izquierdo o rvalue del tipo de ser asignado a. Eso solo deja la cuestión de si devolver una referencia a X, una referencia constante a X o una X (por valor).
Devolver una referencia constante a X es generalmente una mala idea. En particular, se permite que una referencia constante se una a un objeto temporal. El tiempo de vida del temporal se extiende a la duración de la referencia a la que está vinculado, pero no recursivamente a la duración de lo que pueda asignarse. Esto hace que sea más fácil devolver una referencia colgante: la referencia constante se vincula a un objeto temporal. La duración de ese objeto se extiende a la duración de la referencia (que finaliza al final de la función). En el momento en que la función retorna, la vida útil de la referencia y temporal han terminado, entonces lo que se asigna es una referencia colgante.
Por supuesto, devolver una referencia no const no proporciona una protección completa contra esto, pero al menos te hace trabajar un poco más duro. Todavía puede (por ejemplo) definir algún local y devolverle una referencia (pero la mayoría de los compiladores pueden advertir sobre esto también).
Devolver un valor en lugar de una referencia tiene problemas teóricos y prácticos. En el aspecto teórico, normalmente tiene una desconexión básica entre =
y lo que significa en este caso. En particular, cuando la asignación normalmente significa "tomar esta fuente existente y asignar su valor a este destino existente", comienza a significar algo más parecido a "tomar esta fuente existente, crear una copia y asignar ese valor a este destino existente". "
Desde un punto de vista práctico, especialmente antes de que se inventaran las referencias rvalue, eso podría tener un impacto significativo en el rendimiento: la creación de un objeto completamente nuevo en el proceso de copia A a B era inesperado y a menudo bastante lento. Si, por ejemplo, tuviera un vector pequeño y se lo asignara a un vector más grande, esperaría que, como máximo, tome el tiempo para copiar los elementos del pequeño vector más una (pequeña) sobrecarga fija para ajustar el tamaño de el vector de destino.Si eso involucrara dos copias, una desde la fuente a la temperatura, otra desde la temperatura hasta el destino, y (peor) una asignación dinámica para el vector temporal, mi expectativa sobre la complejidad de la operación sería completamente destruida. Para un vector pequeño, el tiempo para la asignación dinámica podría ser muchas veces mayor que el tiempo para copiar los elementos.
La única otra opción (agregada en C++ 11) sería devolver una referencia rvalue. Esto podría conducir fácilmente a resultados inesperados: una asignación encadenada como a=b=c;
podría destruir el contenido de b
y/o c
, lo que sería bastante inesperado.
Eso deja devolver una referencia normal (no una referencia a const, ni a una referencia de valor) como la única opción que (razonablemente) produce lo que la mayoría de la gente normalmente quiere.
no hay tal requisito. Pero si quieres apegarte al principio de la menor sorpresa, devolverás 'A &' como 'a = b' es una expresión lvalue que hace referencia a' a' en caso de que 'a' y' b' sean enteros. – sellibitze
@MattMcNabb ¡Gracias por avisarme! Hará eso – bks
¿Por qué no podemos devolver 'A *' del operador de asignación de copias Supongo que la asignación de encadenamiento aún funcionaría correctamente. ¿Alguien puede ayudar a comprender los peligros de devolver 'A *' si hay alguno. –