Espacios de nombres
Variantes
Acciones

Categorías de valores

De cppreference.com
< cpp‎ | language

Cada expresión en C++ (un operador con sus operandos, un literal, un nombre de variable, etc.) se caracteriza por dos propiedades independientes: un tipo y una categoría de valor. Cada expresión tiene algún tipo no referencia, y pertenece exactamente a una de las tres categorías de valores principales: prvalue, xvalue, y lvalue, que se definen como:

  • un glvalue (lvalue “generalizado”) es una expresión cuya evaluación determina la identidad de un objeto, campo de bits, o función;
  • un prvalue (rvalue “puro”) es una expresión cuya evaluación bien
  • calcula el valor del operando de un operador (dicho prvalue no tiene objeto resultado), o bien
  • inicializa un objeto o campo de bits (dicho prvalue se dice que tiene un objeto resultado). Todas las clases y matrices prvalues tienen un objeto resultado, incluso si se descarta. En ciertos contextos, la materialización temporal ocurre para crear un objeto temporal como resultado;
  • un xvalue (un valor “expirado”) es un glvalue que denota un objeto o campo de bits cuyos recursos se pueden reutilizar;
  • un lvalue (así llamado, históricamente, porque los lvalues podrían aparecer un el lado izquierdo de una expresión de asignación) es un glvalue que no es un xvlaue;
  • un rvalue (así llamado, históricamente, porque los rvalues podrían aparecer en el lado derecho de una expresión de asiganción) es un prvalue o un xvlaue.

Nota: esta taxonomía paso por cambios significativos con la revisiones estándar de C++ anteriores, ver Historia posteriormente para más detalles.

Contenido

[editar] Categorías primarias

[editar] lvalue

Un lvalue es una expresión que identifica a un objeto no temporal o una función no miembro.

Son expresiones lvalue las siguientes:

  • el nombre de una variable, una función , un objeto parámetro de plantilla (desde C++20), o un miembro de datos, independientemente del tipo, como std::cin o std::endl. Incluso si el tipo de variable es una referencia rvalue, la expresión que consiste en su nombre es una expresión lvalue;
  • una expresión de llamada a función o operador sobrecargado, cuyo tipo de retorno es una referencia a lvalue, como std::getline(std::cin, str), std::cout << 1, str1 = str2, o ++it;
  • a = b, a += b, a %= b, y todas las demás expresiones integradas de asignación y asignación compuesta;
  • ++a and --a, las expresiones de pre-incremento y pre-decremento;
  • *p, la expresión de indirección;
  • a[n] y p[n], las expresiones subíndice , donde un operando en a[n] es una matriz lvalue (desde C++11);
  • a.m la expresión miembro de objeto, excepto cuando m es un enumerado miembro o una función miembro no estática, o cuando a es un rvalue y m no es un miembro estático de datos de tipo no referencia;
  • p->m, la expresión miembro de puntero, excepto cuando m es un miembro enumerado o una función miembro no estática;
  • a.*mp la expresión puntero a miembro de objeto, donde a es una lvalue y mp es una puntero a miembro de datos.
  • p->*mp, la expresión puntero a miembro de puntero, donde mp es un puntero a miembro de datos.
  • a, b, la expresión coma, donde b es un lvalue;
  • a ? b : c, la expresión ternaria condicional para algunas b y c (por ejemplo, cuando ambas son lvalue del mismo tipo, para más detalles ver definición);
  • un literal cadena, como "Hola, mundo";
  • una expresión de conversión a tipo referencia lvalue, como static_cast<int&>(x);
  • una expresión de llamada a función o operador sobrecargado, cuyo tipo de retorno es una referencia rvalue a una función.
  • una expresión de cast a tipo rvalue referencia a función, como static_cast<void (&&)(int)>(x).
(desde C++11)


Propiedades:

  • Las mismas que glvalue (ver más abajo):
  • Puede tomarse la dirección de un lvalue: &++i[1] y &std::endl son expresiones válidas.
  • Se puede usar un lvalue modificable como el operando izquierdo de los operadores asignación y asignación compuesta incorporada al lenguaje.
  • Se puede utilizar un lvalue para inicializar una referencia lvalue; esto asocia un nuevo nombre con el objeto identificado por la expresión.

[editar] prvalue

Un prvalue es una expresión que identifica un objeto temporal (o un subobjeto de los mismos) o es un valor no asociado a ningún objeto.

Son expresiones prvalue las siguientes :

  • un literal (excepto los literales cadena), como 42, true o nullptr;
  • una expresión de llamada a función o de operador sobrecargado, cuyo tipo de retorno no es una referencia, como str.substr(1, 2), str1 + str2, o it++;
  • a++ and a--, las expresiones integradas de post-incremento y post-decremento;
  • a + b, a % b, a & b, a << b, y todas las otras expresiones aritméticas integradas;
  • a && b, a || b, !a, las expresiones lógicas integradas.
  • a < b, a == b, a >= b, y todas las demás expresiones de comparación integradass.
  • &a, la expresión dirección de;
  • a.m, la expresión de miembro de objeto, donde m es un miembro enumerado o una función miembro no estática[2] , o cuando a es un rvalue y m un dato miembro no estático de un tipo no referencia (hasta C++11);
  • p->m, la expresión miembro de puntero incorporada, donde m es un miembro enumerado o una función miembro no estática[2];
  • a.*mp, la expresión puntero a miembro de objeto, donde mp es un puntero a una función miembro[2], o donde a es un rvalue y mp es un puntero a datos miembro (hasta C++11);
  • p->*mp, expresión de puntero a miembro de puntero, donde mp es un puntero a una función miembro[2];
  • a, b, la expresión coma incorporada, cuando b es un rvalue;
  • a ? b : c, la expresión ternaria condicional para algunos b y c (ver definición para más detalles);
  • una expresión cast a tipo no referencia, como static_cast<double>(x), std::string{}, o (int)42;
  • el puntero this;
  • un enumerado;
(desde C++11)
(desde C++20)


Propiedades:

  • Las mismas que rvalue (más abajo).
  • Un prvalue no puede ser polimorfico: el tipo dinámico del objeto que identifica siempre es el tipo de la expresión.
  • Un prvalue no clase y no matriz no puede ser calificado. (Nota: una expresión de llamada a función o cast puede resultar en un prvalue de tipo no clase calificado, pero el calificador es eliminado inmediatamente).
  • Un prvalue no puede tener un tipo incompleto (excepto para el tipo void, ver abajo, o cuando se usa en el especificador decltype).

[editar] xvalue

Un xvalue es una expresión que identifica un objeto "que expira", es decir, el objeto desde el que se puede mover. El objeto identificado por una expresión xValue puede ser un temporal sin nombre, un objeto designado en el ámbito, o cualquier otro tipo de objeto, pero si se usa como un argumento de función, xValue siempre se unirá a la sobrecarga de referencia rvalue si está disponible.

Las siguientes son expresiones xvalue:

  • Una expresión de llamada a función u operador sobrecargado, cuyo tipo de retorno es una referencia rvalue a un objeto, como std::move(x);
  • a[n], la expresión subíndice incorporada, donde un operando es una matriz rvalue;
  • a.m, la expresión de miembro de objeto, done a es un rvalue y m es un miembro de datos no estático de tipo no referencia.
  • a.*mp, la expresión de puntero a miembro de objeto, donde a es un rvalue y mp es un puntero a miembro de datos;
  • a ? b : c, la expresión ternaria condicional para algunos b y c (ver definición para más detalles);
  • una expresión cast a referencia rvalue a tipo objeto, como static_cast<char&&>(x);


(desde C++17)


Propiedades:

  • Las mismas que rvalue (ver más abajo).
  • Las mismas que glvalue (ver más abajo).

En particular, como todos los rvalues, xvalues se une a las referencias a rvalue, y como todos los glvalues, xvalues pueden ser polimorficos, y los xvalues no clase pueden ser calificados.

[editar] Categorías mixtas

[editar] glvalue

Una expresión glvalue es lvalue o xvalue.

Propiedades:

  • Un glvalue puede ser convertido implícitamente a un prvalue con la conversión implícita de lvalue a rvalue, de matriz a puntero, o de función a puntero.
  • Un glvalue puede ser polimorfico: el tipo dinámico del objeto que identifica no es necesariamente el tipo estático de la expresión.
  • Un glvalue puede tener un tipo incompleto, donde se permite en la expresión.

[editar] rvalue

Una expresión rvalue es prvalue o xvalue.

Propiedades:

  • La dirección de un rvalue no se puede obtener: &int(), &i++[3], &42, y &std::move(x) son inválidos.
  • No se puede utilizar un rvalue como operando del lado izquierdo de operadores incorporados de asignación o asignación compuesta.
  • Un rvalue se puede utilizar para inicializar una referencia lvalue constante, en cuyo caso el tiempo de vida del objeto identificado por el rvalue se extiende hasta el final del ámbito de la referencia.
  • Un rvalue se puede usar para inicializar una referencia rvalue, en cuyo caso el tiempo de vida del objeto identificado por el rvalue es extendido hasta el final del ámbito de la referencia.
  • Cuando se usa como un argumento de función o cuando dos sobrecargas de la función está disponibles, uno obtiene el parámetro referencia rvalue y el otro obtiene la referencia lvalue a parámetro constante, un rvalue se une a la referencia rvalue sobrecargada (esto es, si están disponibles los constructores copia y movimiento, un argumento rvalue invoca la contructor de movimiento, y del mismo modo con los operadores de asignación de copia y movimiento).
(desde C++11)


[editar] Categorías especiales

[editar] Llamada a función miembro pendiente

Las expresiones a.mf y p->mf, donde mf es una función miembro no estática, y las expresiones a.*pmf y p->*pmf, donde pmf es un puntero a función miembro, se clasifican como expresiones prvalue, pero no se pueden usar para incializar referencias, como argumentos de función, o para cualquier propósito en absoluto, excepto como argumento del lado izquierdo del operador de llamada a función, por ejemplo, (p->*pmf)(args).

[editar] Expresiones void

Expresiones de llamada a función con retorno void, expresiones cast a void, y expresiones throw se clasifican como expresiones prvalue, pero no se puede utilizar para inicializar referencias o como argumentos de función. Se puede usar en contextos de valor descartado (por ejemplo, en una línea propia, como el oprando del lado izquierdo del operador coma, etc.) y en la sentencia return en una función de devuelve void. Además, las expresiones throw se pueden usar como el segundo y el tercer operando del operador condicional ?:.

Las expresiones void no tienen objeto resultado.

(desde C++17)

[editar] Campo de bits

Una expresión que designa un campo de bits (por ejemplo, a.m, donde a es un lvalue de tipo struct A { int m: 3; }) es una expresión lvalue: se puede usar como operando del lado izquierdo del operador de asignación, pero no se puede obtener su dirección y una referencia lvalue no constante no se puede vincular a ella. Una referencia lvalue constante se puede inicializar con un campo de bits lvalue, pero una copia temporal del campo de bits se creará: no se unirá directamente al campo de bits.

[editar] Historia

[editar] CPL

El lenguaje de programación CPL fue el primero en introducir las categorías de valor para expresiones: todas las expresiones CPL se pueden evaluar en el modo de lado derecho, pero solamente ciertas clases de expresiones son válidas en el modo de lado izquierdo. Cuando se evalúa en el modo de lado derecho, una expresión se considera una regla para el cálculo de un valor (el valor del lado derecho, o rvalue). Cuando se evalúa en el modo de lado izquierda una expresión adquiere una dirección (el valor del lado izquierdo, o lvalue). Izquierda y Derecha aquí representaban izquierda de asignación y derecha de asignación.

[editar] C

El lenguaje de programación C sigue una taxonomía similar, excepto que el rol de asignación ya no era significativo: las expresiones en C se dividen entre expresiones lvalue y otras (funciones y valores no objeto), donde lvalue significa una expresión que identifica a un objeto, un valor de localizador[4].

[editar] C++98

C++, previo al 2011, siguió el modelo C, pero restauró el nombre rvalue para las expresiones no lvalue, funciones en lvalues, y añadió la regla por la que las referencia se pueden vincular a lvalues, pero solamente las referencias a constantes se pueden vincular a rvalues. Varias expresiones no lvalue en C se convirtieron en expresiones lvalue en C++.

[editar] C++11

Con la introducción de la semántica de movimientos en C++11, se redefinieron las categorías de valores para caracterizar dos propiedades independientes de las expresiones[5]:

  • tiene identidad: es posible determinar si la expresión se refiere a la misma entidad como otra expresión, como al comparar las direcciones de los objetos o las funciones que identifican (obtenida directa o indirectamente).
  • se puede mover: el constructor de movimiento, el operador de asignación de movimiento, u otra función sobrecargada que implementa la semántica del movimiento puede vincularse a la expresión.

En C++11, las expresiones que:

  • tienen identidad y no se pueden mover se llaman expresiones lvalue;
  • tienen identidad y se pueden mover se llaman expresiones xvalue;
  • no tienen identidad y se puede mover se llaman prvalue (rvalue puro)
  • no tienen identidad y no se pueden mover no se usan[6].

Las expresiones que tienen identidad se llaman expresiones glvalue (generalized lvalue). Lvalues y xvalues son expresiones glvalue.

Las expresiones que se pueden mover se llaman expresiones rvalue. Prvalues y xvlaues son expresiones rvalue.

[editar] C++17

En C++17, la copia elision se hizo pbligatoria en algunas situaciones, y que requiere la separación de las expresiones prvalue de los objetos temporales incializados por ella, resultando el sistema que tenemos hoy. Observe que, en contraste con el esquema de C++11, prvalues ya no se mueve.

[editar] Notas al pie

  1. asumiendo que i tiene un tipo incorporado o el operador de pre-incremento está sobrecargado para devolver por referencia lvalue.
  2. 2,0 2,1 2,2 2,3 Categoría rvalue especial, ver llamada a función miembro pendiente.
  3. Asumiendo i tiene un tipo incorporado o el operador de postincremento no está sobrecargado para retorna una referencia lvalue.
  4. Hay diferentes opiniones en la comunidad de C respecto al significado de lvalue, unos consideran que un lvalue puede ser cualquier clase de localizador de objeto, mientras otros sostienen que un lvalue es significativo en el lado izquierdo de un operador de asignación. El Comité C89 adoptó la definición de lvalue como un localizador de objeto. -- ANSI C Rationale, 6.3.2.1/10.
  5. "New" Value Terminology by Bjarne Stroustrup, 2010.
  6. prvalues constantes (solamente permitido para tipos clase) y xvalues constantes no vincula a las sobrecargas de T&&, pero vinculan sobrecargas de const T&&, que también están clasificados como "constructor de movimiento" y "operador de asiganción de movimiento" por el estándar, satisfaciendo la definición de "se puede mover" para el propósito de esta clasificación. Sin embargo, como las sobrecargas no pueden modificar sus argumentos y no se usan en la practica; en su ausencia prvalues constantes y xvalues constantes vinculan a las sobrecargas de const T&.

[editar] Informe de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
CWG 616 C++11 acceso a miembro y acceso a miembro con puntero a miembro de un rvalue daba como resultado prvalue reclasificado como xvalue
CWG 1213 C++11 el subíndice de una matriz rvalue da como resultado lvalue reclasificado como xvalue

[editar] Ver también

Documentacion C de categorías de valores