error: inicialización no válida de referencia no constante de tipo 'int &' de un rvalue de tipo 'int'

87

Formulario erroneo:

int &z = 12;

Forma correcta:

int y;
int &r = y;

Pregunta :
¿Por qué es incorrecto el primer código? ¿Cuál es el " significado " del error en el título?

Acuario_Chica
fuente
4
Los temporales no se pueden vincular a referencias no constantes. int (12) es temporal en este caso.
Prasoon Saurav
@PrasoonSaurav ¿A qué te refieres con temporal 12? Falta de conceptos aquí (de mi parte :))
Aquarius_Girl
2
Tenga en cuenta que no existe una razón técnica estricta para esta restricción. Habría sido igual de fácil de implementar para permitir referencias mutables a temporales. Prohibirlo es una decisión de diseño de C ++, ya que tal construcción sería un diseño deficiente con un riesgo mucho mayor de ser abusado inadvertidamente que la utilidad genuina. (Solo una vez encontré una necesidad artificial para tal cosa.)
Kerrek SB
@KerrekSB la necesidad más común para un ref unión al objeto rvalue probablemente es(ostringstream() << "x=" << x).str()
curiousguy
@curiousguy: Sí, ese es el contenido del enlace que publiqué.
Kerrek SB

Respuestas:

132

C ++ 03 3.10 / 1 dice: "Cada expresión es un valor l o un valor r". Es importante recordar que lvalor versus rvalority es una propiedad de las expresiones, no de los objetos.

Lvalues ​​nombra objetos que persisten más allá de una sola expresión. Por ejemplo, obj, *ptr, ptr[index], y ++xson todos lvalues.

Los valores R son temporales que se evaporan al final de la expresión completa en la que viven ("en el punto y coma"). Por ejemplo, 1729, x + y, std::string("meow"), y x++son todos rvalues.

El operador de dirección de requiere que su "operando sea un valor l". si pudiéramos tomar la dirección de una expresión, la expresión es un valor l, de lo contrario es un valor r.

 &obj; //  valid
 &12;  //invalid
BruceAdi
fuente
2
" si pudiéramos tomar la dirección de una expresión, la expresión es un valor l, de lo contrario es un valor r " . ¡Si tan solo C ++ fuera así de simple! (pero el matiz no es realmente relevante aquí) "Rvalues ​​son temporales" ¿temporal qué? ¿objetos?
Curioso
2
@curiousguyRvalues ​​son temporales que desaparecen al final de la expresión completa en la que viven. Para responder simplemente por qué el expreso int & = 12;no es válido, el estándar dice que un literal de cadena es un valor l, otros literales son valores r.
BruceAdi
@curiousguy: Sí. Los valores r son objetos temporales , pero no todos los valores r son objetos temporales; algunos ni siquiera son objetos.
Nawaz
" Por ejemplo, 1729, x + y, std :: string (" miau ") y x ++ son valores r . " Pero std::string("meow")construye un objeto de tipo std::stringy produce un valor r que designa este objeto, 1729no tiene efectos secundarios y produce un valor 1729 como un valor de tipo int.
Curioso
1
@curiousguy: La declaración "Lvalues name objects that persist beyond a single expression."es 100% correcta. Por el contrario, su ejemplo (const int &)1es incorrecto, porque NO es un objeto "nombrado".
Nawaz
53
int &z = 12;

En el lado derecho, intse crea un objeto temporal de tipo a partir del literal integral 12, pero el temporal no se puede vincular a una referencia no constante. De ahí el error. Es lo mismo que:

int &z = int(12); //still same error

¿Por qué se crea un temporal? Debido a que una referencia tiene que hacer referencia a un objeto en la memoria, y para que exista un objeto, primero debe crearse. Dado que el objeto no tiene nombre, es un objeto temporal . No tiene nombre. A partir de esta explicación, quedó bastante claro por qué el segundo caso está bien.

Un objeto temporal puede vincularse a una referencia constante, lo que significa que puede hacer esto:

const int &z = 12; //ok

Referencia de C ++ 11 y Rvalue:

En aras de la integridad, me gustaría agregar que C ++ 11 ha introducido rvalue-reference, que puede vincularse a un objeto temporal. Entonces, en C ++ 11, puede escribir esto:

int && z = 12; //C+11 only 

Tenga en cuenta que hay en &&lugar de &. También tenga en cuenta que constya no es necesario, aunque el objeto al que se zune es un objeto temporal creado a partir de un literal integral 12.

Dado que C ++ 11 ha introducido rvalue-reference , de int&ahora en adelante se llama lvalue-reference .

Nawaz
fuente
10

12es una constante en tiempo de compilación que no se puede cambiar a diferencia de los datos a los que hace referencia int&. Lo que puedes hacer es

const int& z = 12;
Michael Krelin - hacker
fuente
2
@curiousguy, sé coherente, has dicho "Tienes que entender que estas son reglas de C ++. Existen y no necesitan ninguna justificación" (sin mencionar la primera edición). ¿Cómo interpretaré tu queja de no dar lo que según tú no es necesario?
Michael Krelin - hacker
2
@ MichaelKrelin-hacker: Técnicamente no, no puede (nunca) vincular una referencia a un valor (o compilar constante de tiempo), el estándar es bastante explícito en cuanto a lo que realmente sucede: de lo contrario, se crea un temporal de tipo "cv1 T1" y inicializado a partir de la expresión del inicializador utilizando las reglas para una inicialización de copia sin referencia (8.5). Entonces, la referencia se vincula al temporal. Es decir, se permite la sintaxis, pero la semántica no es la de vincular una referencia a la constante, sino que la vincula al temporal que se crea implícitamente.
David Rodríguez - dribeas
1
@curiousguy: Las reglas del lenguaje son parte del diseño, y la mayoría de las veces, hay justificaciones de por qué el lenguaje fue diseñado así. En este caso particular, como en C, no se le permite tomar la dirección de un valor (no tiene uno), ni se puede vincular una referencia. Ahora considere una función void f( vector<int> const & ), que es idiomática para pasar un vector que no se va a modificar. El problema ahora es que f( vector<int>(5) )sería incorrecto y el usuario tendría que proporcionar una sobrecarga diferente, lo void f( vector<int> v ) { f(v); }cual es trivial.
David Rodríguez - dribeas
1
... ahora, porque eso sería doloroso para los usuarios del lenguaje, los diseñadores decidieron que el compilador realizaría la operación equivalente por usted, en la llamada f( vector<int>(5) ), el compilador crea un temporal y luego vincula la referencia a ese temporal, y de manera similar si hubo una conversión implícita de 5directamente. Esto permite que el compilador genere una única firma para la función y habilita la implementación de la función por un solo usuario. A partir de ahí, se define un comportamiento similar para el resto de usos de referencias constantes por coherencia.
David Rodríguez - dribeas
1
@ MichaelKrelin-hacker: las referencias son alias a los objetos y un valor no es un objeto. Dependiendo del contexto, las referencias pueden ser solo un alias, el compilador elimina la referencia y solo usa el identificador para significar lo que significó el objeto original ( T const & r = *ptr;, cualquier uso posterior de ren la función puede ser reemplazado por *ptr, y rno necesita existir en tiempo de ejecución) o podría tener que implementarse manteniendo la dirección del objeto al que le asigna un alias (considere almacenar una referencia como miembro de un objeto), que se implementa como un puntero con referencia automática.
David Rodríguez - dribeas
4

Los enlaces de referencia no const y const siguen reglas diferentes

Estas son las reglas del lenguaje C ++:

  • una expresión que consta de un número literal ( 12) es un "rvalue"
  • no está permitido crear una referencia no constante con un rvalue: int &ri = 12;está mal formado
  • se permite crear una referencia constante con un rvalue: en este caso, el compilador crea un objeto sin nombre; este objeto persistirá mientras exista la propia referencia.

Tienes que entender que estas son reglas de C ++. Simplemente son.

Es fácil inventar un lenguaje diferente, digamos C ++ ', con reglas ligeramente diferentes. En C ++ ', estaría permitido crear una referencia no constante con un rvalue. Aquí no hay nada inconsistente o imposible.

Pero permitiría un código arriesgado donde el programador podría no obtener lo que pretendía, y los diseñadores de C ++ decidieron con razón evitar ese riesgo.

curioso
fuente
0

Las referencias son "punteros ocultos" (no nulos) a cosas que pueden cambiar (valores l). No puede definirlos como una constante. Debería ser algo "variable".

EDITAR::

estoy pensando en

int &x = y;

como casi equivalente a

int* __px = &y;
#define x (*__px)

donde __pxes un nombre nuevo, y las #define xobras solo están dentro del bloque que contiene la declaración de xreferencia.

Basile Starynkevitch
fuente
1
Por qué puede, si la referencia es const:)
Michael Krelin - hacker
Pero el ejemplo del cartel no lo fueconst
Basile Starynkevitch
Sí, me refiero a su redacción "las referencias son indicadores de cosas que pueden cambiar", se trata de referencias sin costo.
Michael Krelin - hacker
"Las referencias son" punteros ocultos " " incorrectos " a cosas que pueden cambiar cosas " incorrectas " que pueden cambiar (valores l) " incorrecto
curioso
@Loki: ¿podrías explicar más?
Basile Starynkevitch