¿Es un miembro de una estructura rvalue un rvalue o lvalue?


Una llamada a una función que devuelve una estructura es una expresión rvalue, pero ¿qué pasa con sus miembros?
Esta pieza de código funciona bien con mi compilador g++, pero gcc da un error diciendo "lvalue requerido como operando izquierdo de asignación":

struct A
{
    int v;
};

struct A fun()
{
    struct A tmp;
    return tmp;
}

int main()
{
    fun().v = 1;
}

Gcc trata a fun().v como rvalue, y puedo entenderlo.
Pero g++ no cree que la expresión de asignación sea incorrecta. Eso significa fun1 ().v es lvalue en C++?
Ahora el problema es que busqué en el estándar C++98/03, sin encontrar nada que diga acerca de si fun().v es lvalue o rvalue.
Entonces, ¿qué es?

Author: GManNickG, 2010-02-08

6 answers

Un miembro de una expresión rvalue es un rvalue.

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

Si se declara que E2 tiene tipo "referencia a T", luego E1.E2 es un lvalue [...] - Si E2 es un miembro de datos no estático, y el tipo de E1 es "cq1 vq1 X", y el tipo de E2 es "cq2 vq2 T", el expresión designa el miembro nombrado del objeto designado por el primer expresion. Si E1 es un lvalue, entonces E1.E2 es un lvalue.

 12
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 12:20:31

Editar: 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 integrados, las conversiones del operando izquierdo están restringidas de la siguiente manera: - no se introducen temporarios para sostener el operando izquierdo, y [...]

fun1() debería devolver una referencia. Un tipo de retorno sin referencia / puntero de a la función es un valor r.

3.10 Lvalues y rvalues

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

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

8.3.2 Referencias

2 Un tipo de referencia que se declara usar & se denomina referencia lvalue, y un tipo de referencia que se declara el uso de & & se denomina rvalue referencia. Lvalue referencias y rvalue las referencias son tipos distintos.

 2
Author: dirkgently,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 10:44:17

Este es un buen momento para aprender sobre lo que xvalues y glvalues son.

Rvalues puede ser de dos tipos - prvalues y xvalues. Según el nuevo estándar C++17

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

Así que algo como fun() en su ejemplo se evalúa a un prvalue (que es an rvalue). Esto también nos dice que fun().v no es un prvalue, ya que no es una inicialización vanilla.

Xvalues que también son rvalues se definen así

Un xvalue (un valor "que expira") también se refiere a un objeto, generalmente cerca del final de su vida útil (para que sus recursos se puedan mover, por ejemplo). Ciertos tipos de expresiones que implican referencias rvalue (8.3.2) producen xvalues. [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 rvalues, otra categoría de valor paraguas es un glvalue que es de dos tipos xvalues y el tradicional lvalues.

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

introduzca la descripción de la imagen aquí

La categoría glvalue puede considerarse ampliamente como lo que lvalues eran se supone que significa antes de mover semántica se convirtió en una cosa-una cosa que puede estar en el lado izquierdo de una expresión. glvalue significa lvalue generalizado.

Si miramos la definición de un xvalue, entonces dice que algo es un xvalue si está cerca del final de su vida. En tu ejemplo, fun().v está cerca del final de su vida. Así que sus recursos pueden ser movidos. Y dado que sus recursos se pueden mover no es un lvalue, por lo tanto su expresión encaja en el solo categoría de valor de hoja que permanece-un xvalue.

 1
Author: Curious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-04 02:36:01

He notado que gcc tiende a tener muy pocos reparos sobre el uso de rvalues como lvalues en 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 ver con respecto a lvalues, rvalues y dónde se pueden usar. Es una de las razones por las que estoy tan interesado en esta pregunta sobre rvalues.

 0
Author: Omnifarious,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:44

Se hace obvio cuando se 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 en que el estándar le permite llamar a métodos de miembros en temporales, es decir, puede llamar a miembros no constantes en temporales no constantes.

Ver 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 escribes

struct Foo {};
const Foo foo () { // return a const value
    return Foo();
}

int main () {
    foo() = Foo(); // error
}

Es decir, si dejas que la función foo() devuelve un 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 temporarie:

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
}

Podría definir el tiempo de vida de un temporal dentro de la expresión actual. Y, a continuación, es por eso que también puede modificar las variables miembro; si no podía, que la posibilidad de ser capaz de llamar a los métodos de miembro no const sería llevado ad absurdum.

Editar: Y aquí están los requisitos citas:

12.2 objetos Temporales:

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

Y luego (o mejor, antes)

3.10 Lvalues y rvalues:

  • 10) Un lvalue para un objeto es necesario para modificar el objeto, excepto que un rvalue de tipo clase también se puede usar para modificar su referent bajo 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

 0
Author: Sebastian Mach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 10:15:29

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 debe asignar eiter nueva instancia de A por:

new A()

En este caso, mejor firma

A* f(){ ...

O devuelve una instancia existente, por ejemplo:

static A globalInstance;
A& f(){ 
  return globalInstance;
}
 -2
Author: Dewfy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 08:16:42