No se puede pasar el objeto temporal como referencia

Este es un ejemplo muy mínimo:

class Foo { public: Foo(int x) {}; }; void ProcessFoo(Foo& foo) { } int main() { ProcessFoo(Foo(42)); return 0; } 

Lo anterior comstack bien en Visual Studio, pero genera un error en Linux y Mac.

Comstackr lo anterior genera esto:

 $ g++ -std=c++11 -c newfile.cpp newfile.cpp: In function 'int main()': newfile.cpp:23:23: error: invalid initialization of non-const reference of type 'Foo&' from an rvalue of type 'Foo' ProcessFoo(Foo(42)); ^ newfile.cpp:14:6: note: in passing argument 1 of 'void ProcessFoo(Foo&)' void ProcessFoo(Foo& foo) 

He encontrado tres soluciones:

  1. Evite el uso de una variable temporal en línea para la invocación de ProcessFoo.

Me gusta esto:

 Foo foo42(42); ProcessFoo(foo42); 
  1. ProcessFoo toma una referencia constante: void ProcessFoo(const Foo& foo)

  2. ProcessFoo simplemente deja pasar a Foo por valor. void ProcessFoo(Foo foo)

¿Por qué el comstackdor prohíbe mi código original? (¿De qué se está protegiendo)? ¿Qué pasa con cada una de las tres soluciones anteriores que satisfacen al comstackdor? ¿Qué permitiría MSVC, pero no g ++?

Por diseño, C ++ solo permite pasar un temporal a una referencia constante, valor o referencia de valor r. La idea es que una función que toma un parámetro de referencia no const indique que quiere modificar el parámetro y permitir que regrese a la persona que llama. Hacerlo con un temporal no tiene sentido y probablemente sea un error.

Y no sé qué versión de g ++ está ejecutando. No funciona aquí: http://coliru.stacked-crooked.com/a/43096cb398cbc973

¿Por qué el comstackdor prohíbe mi código original?

Porque está prohibido por el estándar:

8.5.3 Referencias 5

De lo contrario, la referencia será una referencia lvalue a un tipo const non-volátil (es decir, cv1 será const), o la referencia será una referencia rvalue.
[Ejemplo:
double & rd2 = 2.0; // error: no es un valor l y la referencia no es const

¿De qué se está protegiendo?

Modificación inadvertida de un objeto que se va a destruir después de la llamada a la función.

¿Qué pasa con cada una de las tres soluciones anteriores que satisfacen al comstackdor?

1 Crea un objeto nombrado y 3 una copia.
2 Funciona porque la vida útil del objeto simplemente se extiende y los cambios se evitan al mismo tiempo.

¿Qué permitiría MSVC, pero no g ++?

Porque es una extensión de idioma. Property Pages->C/C++->Language->Disable Language Extensions yendo a Property Pages->C/C++->Language->Disable Language Extensions y obtendrá un error.

Una vez que declaraste el prototipo de ProcessFoo como

 void ProcessFoo(Foo& foo) 

Está transmitiendo su intención ya que el parámetro formal “foo” está sujeto a modificaciones ya que no está siendo aprobado por const &.

En el sitio de llamada,

 ProcessFoo(Foo(42)); 

Foo (42) está creando un objeto de stack temporal que no es modificable. Está bien pasar-por-valor o pasar-por-ref-para-const a un método.

A medida que se enlistó a sí mismo, satisfaciendo esas restricciones, hace feliz al comstackdor.

  1. te está dando un objeto que no está generado por el comstackdor y está bajo tu control.
  2. Informa al comstackdor que el método garantiza la const-ness del objeto.
  3. Informa al comstackdor que este objeto (temporal) se pasa por valor y, por lo tanto, no hay problemas.

¿Por qué el comstackdor prohíbe mi código original?

MSVC tiene una extensión que permite que los temporales se unan a las referencias de lvalue no const. Por supuesto, esta no es una característica de conformidad estándar, así que me mantendría alejado de ella para ser portátil. Por ejemplo, no funciona con las últimas versiones de GCC y Clang como has visto.

¿Qué pasa con cada una de las tres soluciones anteriores que satisfacen al comstackdor?

De vuelta en C ++ 03, las expresiones solo pueden ser lvalues ​​o rvalues. Las referencias solo podían designar la “lvaloriedad” de un objeto, por lo que se utilizó con la intención de aliasar un objeto preexistente. Por el contrario, los valores r no existen más allá de la expresión en la que aparecen. Además, el resultado final de las referencias era normalmente copiar o modificar el objeto, y no tiene mucho sentido para el lenguaje modificar un valor r como 55 por ejemplo.

Las reglas le permiten vincular un valor r a una referencia lvalue a const, en cuyo caso la vida útil temporal se extiende a la duración de la referencia. Cuando toma un objeto por valor, el objeto se copia.

Con C ++ 11 tenemos referencias rvalue y xvalues ​​que se hicieron con el propósito de intercambiar la propiedad. Con esto se reduce la utilidad de lvalue-referencias a const. Además, tomar un valor porcentual causa un movimiento si es un valor r.