c++ c++11 language-lawyer xvalue

c++ - ¿Qué expresiones crean xvalues?



c++11 language-lawyer (3)

Estoy tratando de entender los conceptos de C ++ 11.

El borrador estándar que tengo dice:

Un xvalue (un valor "eXpiring") también se refiere a un objeto, generalmente cerca del final de su vida útil (por ejemplo, para poder mover sus recursos). 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 rvalue es un xvalue. —En ejemplo]

OK, entonces, ¿qué son exactamente los "ciertos tipos de expresiones" que producen valores de x? Esta parte de la especificación no detalla una lista de estas expresiones.

Entiendo lvalue y prvalue (al menos creo, entiendo).


Hay una nota no normativa útil en la introducción a §5 (C ++ 11 §5 [expr] / 6):

[Nota: una expresión es un valor x si es:

  • el resultado de llamar a una función, ya sea implícita o explícitamente, cuyo tipo de retorno es una referencia rvalue al tipo de objeto,

  • una conversión a una referencia rvalue al tipo de objeto,

  • una expresión de acceso de miembro de clase que designa un miembro de datos no estáticos de tipo no de referencia en el que la expresión de objeto es un valor x, o

  • Una expresión de .* puntero a miembro en la que el primer operando es un xvalue y el segundo es un puntero al miembro de datos.

En general, el efecto de esta regla es que las referencias de rvalue con nombre se tratan como lvalues ​​y las referencias de rvalue sin nombre a los objetos se tratan como xvalues; Las referencias de rvalue a las funciones se tratan como lvalues ​​ya sea nombrados o no. "Nota final"

Buscando en el resto del §5, esta lista parece exhaustiva. A la lista le sigue un ejemplo:

struct A { int m; }; A&& operator+(A, A); A&& f(); A a; A&& ar = static_cast<A&&>(a);

Las expresiones f() , f().m , static_cast<A&&>(a) y a + a son valores de x. La expresión ar es un lvalor.

Hay dos formas comunes de obtener una expresión xvalue:

  • Usa std::move para mover un objeto. std::move realiza una static_cast a un tipo de referencia rvalue y devuelve la referencia rvalue.

  • Use std::forward para reenviar un valor. std::forward se usa normalmente en una plantilla de función para habilitar el reenvío perfecto de un argumento de función.

    Si el argumento proporcionado a la plantilla de función era un rvalue, el tipo de parámetro será una referencia de rvalue, que es un lvalue. En este caso, std::forward realiza una static_cast a un tipo de referencia rvalue y devuelve la referencia rvalue.

    (Nota: Si el argumento proporcionado a la plantilla de función era un lvalue, el tipo de parámetro será una referencia de lvalue y std::forward devolverá una referencia de lvalue).


La cláusula 5, que describe la sintaxis de las expresiones válidas, enumera para cada sintaxis de expresión las condiciones en las que la expresión es un lvalue, un xvalue o un prvalue. La lista completa de posibles valores de la cláusula 5 es:

5.2.2 párrafo 10: Una llamada de función es ... un xvalue si el tipo de resultado es una referencia de rvalue al tipo de objeto.

(En el lenguaje técnico de la Norma, "tipo de objeto" no significa lo mismo que "tipo de clase". "Tipo de objeto" incluye tipos fundamentales, punteros y matrices, y excluye solo los tipos de función. Una referencia de valor al tipo de función Siempre se trata como un lvalue, no xvalue.)

Las funciones más notables que devuelven una referencia rvalue son, por supuesto, std::move y, a veces, std::forward .

5.2.5 párrafo 4: Si E2 es un miembro de datos no estáticos ... si E1 es un xvalue, entonces E1.E2 es un xvalue

(Por otro lado, una búsqueda de miembro de datos E1->E2 es siempre un valor l.)

De manera similar, si E1 es un xvalue, entonces la búsqueda de miembros de datos E1.*E2 es un xvalue:

5.5 párrafo 6: El resultado de una expresión .* Cuyo segundo operando es un puntero a un miembro de datos es de la misma categoría de valor (3.10) que su primer operando.

Para los distintos tipos de moldes:

  • dynamic_cast<Type>(expr) : 5.2.7 párrafo 2
  • static_cast<Type>(expr) : 5.2.9 párrafo 1
  • reinterpret_cast<Type>(expr) : 5.2.10 párrafo 1
  • const_cast<Type>(expr) : 5.2.11 párrafo 1
  • (Type) expr : 5.4 párrafo 1

la expresión es un xvalue si y solo si Type es una referencia de rvalor al tipo de objeto. Lo mismo es cierto para Type(expr) , ya que

5.2.3 párrafo 1: Si la lista de expresiones [entre paréntesis después de un nombre de tipo] es una expresión única, la expresión de conversión de tipo es equivalente (en definición, y si está definida en significado) a la expresión de conversión correspondiente (5.4).

(Por otro lado, el Type{expr} es siempre un prvalue).

La sección 5.16 del operador condicional termina diciendo que A ? B : C A ? B : C veces puede ser un xvalue si B y / o C es un xvalue. Pero las reglas completas son difíciles de resumir.

Si una expresión termina llamando a una función de operador sobrecargada definida por el usuario, entonces la sección 5.2.2 se aplica a esa expresión, no a la que describe el comportamiento del operador incorporado. (Vea la expresión a + a en el ejemplo que publicó @James).


Lo que deduzco de lo que he leído es que llamar a xvalor es una forma elegante de decir:

Un xvalue es solo un rvalue cuyo almacenamiento puede haber sido desasignado, por lo que usarlo significa que debe verificar su existencia por sí mismo.

Generalmente, es uno o más niveles de direccionamiento indirecto lejos de un valor real.

Por el contrario, se garantiza que un valor de rg tiene un espacio de almacenamiento existente mientras esté dentro del alcance.

Puede que me equivoque, pero esto es lo que he entendido.