c++ operator-overloading language-design rvalue temporary-objects

c++ - ¿Por qué se permite T()=T()?



operator-overloading language-design (5)

Creo que la expresión T() crea un rvalue (según el estándar). Sin embargo, el siguiente código se compila (al menos en gcc4.0):

class T {}; int main() { T() = T(); }

Sé que técnicamente esto es posible porque las funciones miembro se pueden invocar en temporarios y lo anterior es simplemente invocar al operador = en el valor temporal creado a partir de la primera T() .

Pero conceptualmente, esto es como asignar un nuevo valor a un valor. ¿Hay una buena razón por la que esto está permitido?

Edición: la razón por la que me parece extraño es que está estrictamente prohibido en los tipos incorporados, pero permitido en los tipos definidos por el usuario. Por ejemplo, int(2) = int(3) no se compilará porque eso es un "lvalue inválido en la asignación".

Entonces, supongo que la verdadera pregunta es, ¿fue este comportamiento un tanto inconsistente incorporado al lenguaje por una razón? ¿O está ahí por alguna razón histórica? (Por ejemplo, sería conceptualmente más sólido permitir que solo se invocen funciones miembro const en expresiones de valor, pero eso no se puede hacer porque eso podría romper algún código existente).


Desde un punto de vista, es inconsistente, pero se pasa por alto cómo es consistente: 1) los ints y otros tipos integrados todavía se comportan como lo hacen en C, 2) operator = on class-types se comporta como cualquier otro método sin el requiriendo otro caso especial.

La compatibilidad con C ha sido altamente valorada desde el comienzo de C ++, y C ++ posiblemente no estaría aquí hoy sin ella. Así que esa parte es generalmente una buena cosa.

El segundo punto es subestimado. Operador de caja no especial = permite que el código sin sentido "funcione", pero ¿por qué nos importa el código sin sentido en primer lugar? Basura dentro basura fuera. Las reglas actuales le dan un significado definido (UB aquí sería malo) con un costo insignificante, por lo que he visto.

Dados mis aburridos, las cosas se simplificarían aún más, por lo que se permitiría int() = int() . C ++ 0x comienza a dirigirse en esa dirección con rvalue-references, prvalues, etc.


Es por esto que se pueden implementar varias clases en la biblioteca estándar. Considere, por ejemplo, std::bitset<>::operator[]

// bit reference: class reference { friend class bitset; reference(); public: ˜reference(); reference& operator=(bool x); // for b[i] = x; reference& operator=(const reference&); // for b[i] = b[j]; bool operator˜() const; // flips the bit operator bool() const; // for x = b[i]; reference& flip(); // for b[i].flip(); }; reference operator[](size_t pos); // for b[i];

Si haces bits[i] = true , asignas exactamente algún valor a un valor de tipo de clase. El proxy que devuelve el operator[] puede acceder a los bits que se empaquetan de forma eficiente en enteros.


Esto se permite únicamente por la sobrecarga del operador y la posibilidad de que usted sobrecargue al operator = para hacer algo más sofisticado, como imprimir en la consola, bloquear un mutex o cualquier otra cosa.


Puede restringir operator = para que funcione solo en lvalues ​​en C ++ 0x:

class T { public: T& operator=(const T&) & = default; };


Sí, está asignando un nuevo valor a un valor de r. Más precisamente, está llamando a la función operator = member en un rvalue. Como no está utilizando el operador de asignación integrado, ¿por qué cree que esto debería ser un problema? operator = es una función miembro de la clase, que en la mayoría de los aspectos es similar a cualquier otra función miembro de la clase, incluido el hecho de que se puede llamar en rvalues.

Probablemente también debería tener en cuenta el hecho de que "ser un rvalue" es una propiedad de una expresión, no una propiedad de un objeto. Es cierto que la expresión T() evalúa como un valor de r. Sin embargo, el objeto temporal que produce la expresión T() sigue siendo un objeto , al que también se puede acceder como un valor l. Por ejemplo, se puede llamar a otra función miembro sobre el resultado de la asignación, y verá el valor "nuevo" (recién asignado) del objeto temporal a través de *this lvalue

(T() = T()).some_member_function();

También puede extender la vida útil del temporizador adjuntando una const-referencia a ella const T& r = T() = T(); y el valor visto a través de r será el "nuevo" valor del objeto. Como Johannes señaló correctamente en su comentario, esto no lo adjuntará a un documento temporal.