c++ structure lvalue rvalue

c++ - ¿Es un miembro de una estructura de rvalue un rvalue o lvalue?



structure (6)

Edit: Ok, supongo que finalmente tengo algo del estándar:

Tenga en cuenta que v es de tipo int que tiene un operador de asignación incorporado:

13.3.1.2 Operadores en expresiones

4 Para los operadores de asignación incorporados, las conversiones del operando izquierdo están restringidas de la siguiente manera: - no se introducen temporarios para mantener el operando izquierdo, y [...]

fun1() debe devolver una referencia. Un tipo de retorno de no referencia / puntero de una función es un valor r.

3.10 Valores y valores

5 El resultado de llamar a una función que no devuelve una referencia de lvalue es un rvalue [...]

Así, fun1().v es un rvalue.

8.3.2 Referencias

2 Un tipo de referencia que se declara usando & se llama una referencia de valor l, y un tipo de referencia que se declara usando && se llama una referencia de valor. Las referencias de valores y las referencias de valores son tipos distintos.

Una llamada de función que devuelve una estructura es una expresión rvalue, pero ¿qué pasa con sus miembros?
Este fragmento de código funciona bien con mi compilador g ++, pero gcc da un error que dice "lvalue required as left operand of asignación":

struct A { int v; }; struct A fun() { struct A tmp; return tmp; } int main() { fun().v = 1; }

gcc trata fun().v como rvalue, y puedo entenderlo.
Pero g ++ no cree que la expresión de asignación sea incorrecta. ¿Significa eso que fun1 (). V es lvalue en C ++?
Ahora el problema es que busqué en el estándar C ++ 98/03, sin encontrar nada que diga si fun().v es lvalue o rvalue.
¿Así que qué es lo?


Este es un buen momento para aprender sobre los valores de xvalues y glvalues .

Los valores pueden ser de dos tipos: valores y valores . Según el nuevo estándar C ++ 17.

Un prvalue es una expresión cuya evaluación inicializa un objeto, campo de bits u operando de un operador, según lo especificado por el contexto en el que aparece.

así que algo como fun() en tu ejemplo se evalúa como un prvalue (que es un rvalue). Esto también nos dice que fun().v no es un prvalue, ya que no es una inicialización de vainilla.

Los valores de X que también son valores se definen como tal

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). Ciertos tipos de expresiones que involucran referencias de valores (8.3.2) producen valores de x. [Ejemplo: el resultado de llamar a una función cuyo tipo de retorno es una referencia rvalue a un tipo de objeto es un xvalue (5.2.2). - ejemplo final]

Además de los valores, otra categoría de valor de sombrilla es un valor de glival que puede ser de dos tipos xvalues y los valores tradicionales.

En este punto hemos definido las categorías de valores esenciales. Esto se puede visualizar como tal.

En términos generales, se puede pensar que la categoría glvalue significa lo que se supone que significan lvalues antes de que la semántica de movimiento se convierta en una cosa, una cosa que puede estar en el lado izquierdo de una expresión. glvalue significa lvalue generalizado.

Si observamos la definición de un xvalue , entonces dice que algo es un xvalue si está cerca del final de su vida útil. En su ejemplo, fun().v está cerca del final de su vida útil. Así que sus recursos pueden ser movidos. Y como sus recursos se pueden mover, no es un lvalor, por lo que su expresión se ajusta a la única categoría de valor de hoja que queda: un xvalor .


Me he dado cuenta de que gcc tiende a tener muy pocas funciones sobre el uso de rvalues ​​como valores de l en las expresiones de asignación. Esto, por ejemplo, compila muy bien:

class A { }; extern A f(); void g() { A myA; f() = myA; }

Por qué eso es legal y esto no lo es (es decir, no compila), aunque realmente me confunde:

extern int f(); void g() { f() = 5; }

En mi humilde opinión, el comité estándar tiene algunas explicaciones que hacer con respecto a los valores, valores y dónde se pueden utilizar. Es una de las razones por las que estoy tan interesado en esta pregunta sobre los valores .


Se vuelve obvio cuando considera que el compilador generará un constructor predeterminado, un constructor de copia predeterminado y un operador de asignación de copia predeterminado para usted, en caso de que su estructura / clase no contenga miembros de referencia. Luego, piense que el estándar le permite llamar métodos de miembro en temporarios, es decir, puede llamar a miembros no constantes en temporarios no const.

Vea este ejemplo:

struct Foo {}; Foo foo () { return Foo(); } struct Bar { private: Bar& operator = (Bar const &); // forbid }; Bar bar () { return Bar(); } int main () { foo() = Foo(); // okay, called operator=() on non-const temporarie bar() = Bar(); // error, Bar::operator= is private }

Si tú escribes

struct Foo {}; const Foo foo () { // return a const value return Foo(); } int main () { foo() = Foo(); // error }

es decir, si deja que la función foo () devuelva una const temporal, entonces se produce un error de compilación.

Para completar el ejemplo, aquí está cómo llamar a un miembro de una const temporaria:

struct Foo { int bar () const { return 0xFEED; } int frob () { return 0xFEED; } }; const Foo foo () { return Foo(); } int main () { foo().bar(); // okay, called const member method foo().frob(); // error, called non-const member of const temporary }

Puede definir la duración de un temporal para que esté dentro de la expresión actual. Y es por eso que también puedes modificar las variables miembro; Si no pudiera, la posibilidad de poder llamar a los métodos de los miembros no constantes sería dirigida ad absurdum.

Edición: Y aquí están las citas requeridas:

12.2 Objetos temporales:

  • 3) [...] Los objetos temporales se destruyen como último paso en la evaluación de la expresión completa (1.9) que (léxicamente) contiene el punto donde se crearon. [...]

y luego (o mejor dicho, antes)

3.10 Valores y valores:

  • 10) Es necesario un valor de un objeto para modificar el objeto, excepto que también se puede usar un valor de tipo de clase para modificar su referente en ciertas circunstancias. [Ejemplo: una función miembro llamada para un objeto (9.3) puede modificar el objeto. ]

Y un ejemplo de uso: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Parameter


Tu código no tiene escena. La estructura devuelta se asigna en la pila, por lo que el resultado de la asignación se perderá inmediatamente.

Su función debería aún asignar una nueva instancia de A por:

new A()

En este caso mejor firma.

A* f(){ ...

O devolver instancia existente, por ejemplo:

static A globalInstance; A& f(){ return globalInstance; }


Un miembro de una expresión de valor es un valor de r.

Los estados estándar en 5.3.5 [expr.ref]:

Si se declara que E2 tiene el tipo "referencia a T", entonces E1.E2 es un lvalor: si E2 es un miembro de datos no estáticos, y el tipo de E1 es "cq1 vq1 X", y El tipo de E2 es "cq2 vq2 T", la expresión designa al miembro nombrado del objeto designado por la primera expresión. Si E1 es un lvalue, entonces E1.E2 es un lvalue.