En los últimos post, referencias y cuándo usar referencias, hemos hablado sobre esta construcción del lenguaje C++.
En C++ una referencia no es algo trivial como ocurre en Java o en C#. En C++ una referencia es otra herramienta junto con las variables y los punteros para gestionar la memoria.
Siguiendo con la idea original de realizar una crítica al libro de Stroustup… ¿qué podemos concluir? ¿se explica mal el texto? ¿resulta confuso o difícil de entender? A mi juicio, no. Todo está bastante claro.
Si bien, el único punto remarcable es el por qué sólo se permiten literales en referencias constantes. Acerca de esto, el texto de Stroustup, en el aparatado 5.5, afirma:
Las referencias a variables y las referencias constantes se diferencian debido a que la introducción de un temporal en el caso de la variable es altamente conducente a errores; una asignación a la variable se convertiría en una asignación al temporal, que desaparecerá próximamente. No existe dicho problema para las referencias a constantes, y las referencias a constantes suelen ser importantes como parámetros de función (…).
En mi opinión era más fácil explicarlo como un problema de incompatibilidad de tipos (los literales son const luego la referencia debe ser const) pero esto no es lo que Stroustup quiere decir con el párrafo anterior. De hecho, yo tardé bastante en entenderlo y no fue hasta mucho después, al final del aparatado 7.2, que me dieron un argumento para entenderlo:
La prohibición de las conversiones para parámetros por referencia no-const (…) evita la posibilidad de errores tontos derivados de la introducción de temporales. Por ejemplo:
void actualiza(float &i); void g(double d, float r) { actualiza(2.0f); // error: parámtro const actualiza(r); // pasa por referencia r actualiza(d); // error: se requiere conversión de tipos }Si se hubiesen permitido estas llamadas, actualiza() habría actualizado silenciosamente temporales que se borrarían inmediatamente. Habitualmente, esto sería una sorpresa desagradable para el programador.
El caso de actualiza(2.0f) hubiese resultado un tanto extraño.
¿Cómo narices actualizas un literal? Es más, ¿qué significa actualizar un literal?
O____O
Ahí la primera inconsistencia. El temporal se hubiese creado, hubiera tomado el valor 2.0 y luego podría haberse modificado en la función pero… ¿cómo se transmitiría ese cambio fuera de la función? Opino que lo más sencillo hubiera sido aludir a la incompatibilidad de tipos.
En el caso de actualiza(d), se hubiera creado el temporal e inicializado (por copia del valor, no enlace) con el valor de float(d). Luego la referencia i se hubiese enlazado con el temporal pero no con el parámetro d. Los accesos a i hubiesen resultado en accesos al temporal por lo que tras modificarse en el cuerpo de la función y destruirse al cabo de la misma, d habría quedado invariable.
¿Se podría haber solucionado de otra forma? ¿Es la introducción de temporales la mejor forma de lidiar con estas situaciones?
No lo sé, pero sí sé que la explicación por incompatibilidad de tipos era consistente y mucho más sencilla.