INTRODUCCIÓN
ISOC ++ 11 (oficialmente ISO / IEC 14882: 2011) es la versión más reciente del estándar del lenguaje de programación C ++. Contiene algunas características y conceptos nuevos, por ejemplo:
- referencias de valor
- Categorías de valor de expresión xvalue, glvalue, prvalue
- mover semántica
Si quisiéramos comprender los conceptos de las nuevas categorías de valores de expresión, debemos ser conscientes de que existen referencias de valor y valor. Es mejor saber que los valores se pueden pasar a referencias de valor no constantes.
int& r_i=7; // compile error
int&& rr_i=7; // OK
Podemos obtener cierta intuición de los conceptos de categorías de valores si citamos la subsección titulada Lvalues and rvalues del borrador de trabajo N3337 (el borrador más similar al estándar ISOC ++ 11 publicado).
3.10 Valores y valores [basic.lval]
1 Las expresiones se clasifican de acuerdo con la taxonomía en la Figura 1.
- Un valor l (llamado históricamente porque los valores l podrían aparecer en el lado izquierdo de una expresión de asignación) designa una función o un objeto. [Ejemplo: si E es una expresión de tipo puntero, entonces * E es una expresión de valor que se refiere al objeto o función a la que E apunta. Como otro ejemplo, el resultado de llamar a una función cuyo tipo de retorno es una referencia de lvalue es un lvalue. —Ejemplo]
- Un valor x (un valor "eXpiring") también se refiere a un objeto, generalmente cerca del final de su vida útil (por ejemplo, para que sus recursos se puedan mover). Un xvalue es el resultado de ciertos tipos de expresiones que involucran referencias de rvalue (8.3.2). [Ejemplo: el resultado de llamar a una función cuyo tipo de retorno es una referencia de valor r es un valor x. —Ejemplo]
- Un valor gl (valor "generalizado") es un valor l o un valor x.
- Un valor r (llamado históricamente porque los valores pueden aparecer en el lado derecho de una expresión de asignación) es un valor x, un
objeto temporal (12.2) o subobjeto, o un valor que no está
asociado con un objeto.
- Un valor prva (valor "puro") es un valor r que no es un valor x. [Ejemplo: el resultado de llamar a una función cuyo tipo de retorno no es una
referencia es un prvalue. El valor de un literal como 12, 7.3e5 o
verdadero también es un prvalue. —Ejemplo]
Cada expresión pertenece exactamente a una de las clasificaciones fundamentales en esta taxonomía: lvalue, xvalue o prvalue. Esta propiedad de una expresión se llama categoría de valor.
Pero no estoy muy seguro de que esta subsección sea suficiente para comprender los conceptos claramente, porque "por lo general" no es realmente general, "cerca del final de su vida útil" no es realmente concreto, "involucrar referencias de valor" no es realmente claro, y "Ejemplo: el resultado de llamar a una función cuyo tipo de retorno es una referencia rvalue es un xvalue". Suena como una serpiente mordiéndose la cola.
CATEGORÍAS DE VALOR PRIMARIO
Cada expresión pertenece exactamente a una categoría de valor primario. Estas categorías de valor son lvalue, xvalue y prvalue.
lvalues
La expresión E pertenece a la categoría lvalue si y solo si E se refiere a una entidad que YA ha tenido una identidad (dirección, nombre o alias) que la hace accesible fuera de E.
#include <iostream>
int i=7;
const int& f(){
return i;
}
int main()
{
std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.
i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression i in this row refers to.
int* p_i=new int(7);
*p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
*p_i; // ... as the entity the expression *p_i in this row refers to.
const int& r_I=7;
r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
r_I; // ... as the entity the expression r_I in this row refers to.
f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
i; // ... as the entity the expression f() in this row refers to.
return 0;
}
xvalores
La expresión E pertenece a la categoría xvalue si y solo si es
- el resultado de llamar a una función, ya sea implícita o explícitamente, cuyo tipo de retorno es una referencia de valor al tipo de objeto que se devuelve, o
int&& f(){
return 3;
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
return 0;
}
- una conversión a una referencia rvalue al tipo de objeto, o
int main()
{
static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).
return 0;
}
- una expresión de acceso de miembro de clase que designa un miembro de datos no estático de tipo sin referencia en el que la expresión de objeto es un valor x, o
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
return 0;
}
- una expresión de puntero a miembro en la que el primer operando es un valor xy el segundo operando es un puntero al miembro de datos.
Tenga en cuenta que el efecto de las reglas anteriores es que las referencias de valor r con nombre a los objetos se tratan como valores y las referencias de valor r sin nombre a los objetos se tratan como valores x; Las referencias de rvalue a las funciones se tratan como valores ya sean nombrados o no.
#include <functional>
struct As
{
int i;
};
As&& f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
As&& rr_a=As();
rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.
return 0;
}
prvalues
La expresión E pertenece a la categoría prvalue si y solo si E no pertenece ni al lvalue ni a la categoría xvalue.
struct As
{
void f(){
this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
}
};
As f(){
return As();
}
int main()
{
f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.
return 0;
}
CATEGORÍAS DE VALOR MIXTO
Hay otras dos categorías importantes de valor mixto. Estas categorías de valor son rvalue y glvalue.
valores
La expresión E pertenece a la categoría rvalue si y solo si E pertenece a la categoría xvalue, o a la categoría prvalue.
Tenga en cuenta que esta definición significa que la expresión E pertenece a la categoría rvalue si y solo si E se refiere a una entidad que no ha tenido ninguna identidad que la haga accesible fuera de E YET.
valores
La expresión E pertenece a la categoría glvalue si y solo si E pertenece a la categoría lvalue, o a la categoría xvalue.
UNA REGLA PRÁCTICA
Scott Meyer ha publicado una regla práctica muy útil para distinguir los valores de los valores.
- Si puede tomar la dirección de una expresión, la expresión es un valor l.
- Si el tipo de una expresión es una referencia de valor de l (por ejemplo, T & o const T &, etc.), esa expresión es un valor de l.
- De lo contrario, la expresión es un valor r. Conceptualmente (y típicamente también de hecho), los valores corresponden a objetos temporales, como los devueltos por funciones o creados a través de conversiones de tipo implícitas. La mayoría de los valores literales (p. Ej., 10 y 5.3) también son valores.