Repaso a C++11: L-Values y R-Values

Ya hace un tiempo que se aprobó el nuevo estándar de C++ y parece que a los programadores aún nos da miedo usar éstas nuevas características. Muchos por desconocimientos, otros por compatibilidad. Sea como sea hoy en día los compiladores más usados para el software cotidiano (Visual C++ y GCC) implementan la mayoría de las nuevas funciones. Ahora si el problema es el conocimiento vamos a darle un repaso a las novedades más interesantes de C++11 en una serie de artículos.

Aunque la serie trata de C++11 vamos a empezar con un concepto que no es nuevo, pero que adquiere un gran significado y potencia en la nueva versión de C++ los L-Values y los R-Values.

Los R-Valores son aquellos que solo pueden estar a la derecha de una expresión y que carecen de sentido a la izquierda. Veamos el siguiente ejemplo:

int x;
1 = x;
&x = 3;

Es obvio que eso no se puede hacer, no podemos asignar un valor a un literal ni tampoco a un objeto temporal como es en este caso la dirección de memoria que creamos.

Ciertos operadores como el de asignación de nuestro ejemplo necesita un L-Value a la izquierda y un R-Value a la derecha, como habrás imaginado L viene de Left y R de Right, derecha e izquierda.

Cualquier L-Value se puede comportar siempre como un R-Value, esto que puede sonar raro es lo más obvio y natural que llevas haciendo toda la vida.

int x, y = 4;
x = y;

En este ejemplo en la primera línea tanto x como y son L-Values ya que están en la parte izquierda de la expresión, pero el la segunda línea y actúa como R-Value ya que está a la derecha.

Como L-Value tenemos principalmente:

  • Los nombres de variables y referencias

  • El resultado de desreferenciar un puntero con el operator * o []

  • El retorno de una función que devuelva una referencia

  • Un cast a un tipo referencia

Por el contrario como R-Value tenemos:

  • Literales, excepto las cadenas, que son arrays

  • El resultado de cualquier operador aritmético o lógico

  • El retorno de una función que no devuelva una referencia

  • Cualquier objeto temporal, es decir que no tenga nombre un propio

  • Un cast que no sea a tipo referencia

Hay tres cosas básicas que puedes hacer con un L-Value que no puedes hacer con un R-Value.

  • Asignarlos a otra cosa con el operador =

  • Obtener su dirección con el operador &

  • Usarlos para inicializar una referencia

Para estas reglas existe una excepción y es que podemos usar R-Values para inicializar referencias constantes. Si el R-Value no es un objeto temporal creado por medio de otra función o clase se creará uno con una copia de la expresión y la referencia se vinculará a él. La vida de este objeto temporal estará ligada a la de la referencia, cuando ésta sea destruida se destruirá el objeto temporal.

Esto es muy útil para evitar copias extras en el retorno de funciones

std::string cadena();
const std::string &c = cadena();

Obviamente seguro que esta optimización la haría el propio compilador, pero nunca está demás saberlo.

Mayor utilidad tiene, quizás, cuando se pasa un parámetro con conversión automática de tipo.

 void Funcion(const std::string &nombre);
Funcion("foo.txt");

Aquí estamos asignando un literal (el puntero char* creado) a una referencia, si quitáramos el const del parámetro de la función nos daría un error.

Con eésto terminamos el repaso de L-Values y R-Values. Puede que parezcan conceptos obvios, pero viene bien tenerlos claros porque en los próximos artículos veremos las novedades que aporta C++11 a este campo.

Ver todos los comentarios en https://www.genbeta.com

VER 0 Comentario

Portada de Genbeta