terminology - para - a menudo se usan términos poco definidos: lvalue
palabras matematicas con j (7)
La "L" en lvalue generalmente se describe como "ubicación". Un lvalue especifica la ubicación de algo; como señaló otro respondedor, los valores l normalmente pueden tener su dirección tomada. Es por eso que los literales numéricos y los valores de retorno de la función de no referencia no son valores.
La L solía representar "izquierda" hasta que se introdujo const en C ++. Los valores l de Const no pueden aparecer en el lado izquierdo de una tarea.
¿Qué es un lvalue?
Tradicionalmente es el lado izquierdo del operador "=". Sin embargo, con el tiempo, el significado de "lvalue" / "rvalue" cambió. C ++ agregó el término de un "lvalue no modificable" que es cualquier lvalue que no puede asignarse a: las matrices y las variables que están calificadas con "const" son dos ejemplos. En C, no se puede asignar a ningún valor r (ver a continuación). Del mismo modo, en C ++, no puede asignar valores r que no sean de algún tipo de clase definido por el usuario.
Puede decir que "lvalue" es una expresión que nombra un objeto que persiste en el tiempo y ocupa una ubicación de almacenamiento. Si no puedes asignar a esa expresión no es importante para esa clasificación. Una referencia, en particular, también es un valor l, porque tiene un nombre que persiste en el tiempo. Todos los siguientes son valores l, porque todos se refieren a objetos con nombre. También tenga en cuenta que un const
no tiene ningún efecto sobre el valor l.
int a; lvalue: a;
lvalue: ++a;
int a[2]; lvalue: a;
int &ra = a; lvalue: ra;
int *pa = &a; lvalue: *pa;
El término "rvalue" se usa para cosas como los valores literales y del enumerador y para los temporales que no disfrutan de la diversión de tener una vida larga y se destruyen de inmediato al final de una expresión completa. Para los valores, no es importante el aspecto de la persistencia, sino el aspecto del valor. Las funciones en C ++ son lvalues, porque son persistentes y tienen una dirección, aunque no sean objetos. Los dejé en la visión general anterior de lvalues, porque es más fácil captar valores l cuando solo se tienen en cuenta los objetos. Todos los siguientes son valores r:
enum { FOO, BAR }; rvalue: BAR;
int a[2]; rvalue: (a+1);
rvalue: 42;
int a; rvalue: a++; // refering to a temporary
struct mystruct { }; mystruct f() { return mystruct(); } rvalue: f();
Por cierto, a menudo tienes un lvalue, pero un operador necesita un valor r. Por ejemplo, el operador binario incorporado "+" agrega dos valores. Una expresión lvalue primero y para todos especifica una ubicación donde primero se debe leer un valor. Entonces, cuando agrega dos variables, se lleva a cabo una conversión de "lvalue a rvalue". El Estándar dice que el valor contenido en una expresión lvalue es su resultado rvalue:
int a = 0, b = 1;
int c = a + b; // read values out of the lvalues of a and b.
Otros operadores no toman rvalue, sino lvalues. No leen un valor. Un ejemplo es el operador de dirección, &
. No puede tomar la dirección de una expresión rvalue. Algunos rvalues ni siquiera son objetos: no ocupan ningún espacio de almacenamiento. Los ejemplos son nuevamente, literales (10, 3.3, ...) y valores del enumerador.
¿Cómo es eso de miedo útil?
Bueno, tiene varias ventajas para tener la distinción de lvalue y rvalue
- Permitir que el compilador omita tomar almacenamiento para valores r y usar registros / memoria de sólo lectura para valores escalares
- Señalar las expresiones como esquivo: los valores no durarán mucho
- Permite una semántica de copia eficiente para el compilador internamente y en c ++ 1x también está expuesto al programador (ver semántica de movimiento y referencias de valor r): podemos robar recursos de valores que de todos modos se van a destruir.
- Permite construir reglas sobre esa propiedad
- No se permite generar valores desde objetos aún no inicializados a los que se hace referencia a valores l. Pero lvalues puede referirse a objetos no inicializados bien
- los valores nunca pueden ser polimórficos. Su tipo estático también debe ser su tipo dinámico: Simplifica las reglas para el operador typeid.
... Hay más que eso, lo siento ...
Un lvalue es un valor que se puede asignar a:
lvalue = rvalue;
Es la abreviatura de "valor de la izquierda" o "valor de la izquierda" y es básicamente el valor a la izquierda del signo =
, es decir, el valor que le asigna algo.
Como un ejemplo de lo que no es un lvalue (es decir, rvalue solamente):
printf("Hello, world!/n") = 100; // WTF?
Ese código no funciona porque printf()
(una función que devuelve un int
) no puede ser un lvalue, solo un valor r.
Un simple ejemplo de lo que definitivamente no es un valor:
3 = 42;
Una de las mejores explicaciones que conozco se puede encontrar en este artículo sobre las referencias RValue .
Otra forma de determinar si una expresión es un valor l es preguntar "¿Puedo tomar su dirección?". Si puedes, es un lvalue. Si no puedes, es un valor. Por ejemplo, & obj, & * ptr, & ptr [índice], y & ++ x son todos válidos (aunque algunas de esas expresiones son tontas), mientras que & 1729, & (x + y), & std :: string ("miau "), y & x ++ son todos inválidos. ¿Por qué funciona esto? El operador de dirección requiere que su "operando sea un valor l" (C ++ 03 5.3.1 / 2). ¿Por qué requiere eso? Tomar la dirección de un objeto persistente está bien, pero tomar la dirección de un temporal sería extremadamente peligroso, porque los temporales se evaporan rápidamente.
De este articulo Dado que el OP fue un poco flojo al hacer su pregunta (aunque algunas personas no están de acuerdo, ver comentarios), también seré flojo y simplemente pegaré toda la parte relevante aquí, probablemente rompiendo algunas leyes de derechos de autor.
Un objeto es una región de almacenamiento que puede examinarse y almacenarse. Un lvalue es una expresión que se refiere a tal objeto. Un lvalue no necesariamente permite la modificación del objeto que designa. Por ejemplo, un objeto const es un valor l que no se puede modificar. El término valor l modificable se usa para enfatizar que el valor l permite que el objeto designado sea cambiado y examinado. Los siguientes tipos de objetos son lvalues, pero no lvalues modificables:
- Un tipo de matriz
- Un tipo incompleto
- Un tipo const-calificado
- Un objeto es una estructura o tipo de unión y uno de sus miembros tiene un tipo const-calificado
Debido a que estos lvalues no son modificables, no pueden aparecer en el lado izquierdo de una declaración de asignación.
En C ++, una llamada a función que devuelve una referencia es un valor l. De lo contrario, una llamada a función es una expresión rvalue. En C ++, cada expresión produce un valor l, un valor r o ningún valor.
Ciertos operadores requieren lvalues para algunos de sus operandos. La siguiente tabla enumera estos operadores y restricciones adicionales sobre su uso.
Operator Requirement & (unary) Operand must be an lvalue. ++ -- Operand must be an lvalue. This applies to both prefix and postfix forms. = += -= *= %= >= &= ^= |= Left operand must be an lvalue.
Por ejemplo, todos los operadores de asignación evalúan su operando correcto y asignan ese valor a su operando izquierdo. El operando izquierdo debe ser un valor l modificable o una referencia a un objeto modificable.
El operador de dirección (&) requiere un lvalor como operando, mientras que los operadores de incremento (++) y de decremento (-) requieren un valor l modificable como operando.
Algo que aparece en el lado izquierdo de una tarea, es decir, algo a lo que se puede asignar.
Tenga en cuenta que en C ++ una llamada a función puede ser un valor l si:
int & func() {
static int a = 0;
return a;
}
entonces:
func() = 42; // is legal (and not uncommon)