erro: inicialização inválida de referência não const do tipo 'int &' de um rvalue do tipo 'int'

87

Formulário errado:

int &z = 12;

Forma correta:

int y;
int &r = y;

Pergunta :
Por que o primeiro código está errado? Qual é o " significado " do erro no título?

Aquarius_Girl
fonte
4
Os temporários não podem ser vinculados a referências não constantes. int (12) é temporário neste caso.
Prasoon Saurav
@PrasoonSaurav O que você quer dizer com 12 temporário? Falta de conceitos aqui (da minha parte :))
Aquarius_Girl
2
Observe que não há uma razão técnica estrita para essa restrição. Teria sido tão fácil de implementar para permitir referências mutáveis ​​a temporários. Proibir isso é uma decisão de design do C ++, uma vez que tal construção seria um design ruim, com muito maior risco de ser inadvertidamente abusado do que a utilidade genuína. (Só uma vez encontrei uma necessidade artificial para tal coisa.)
Kerrek SB
@KerrekSB a necessidade mais comum de um objeto binding ref to rvalue provavelmente é(ostringstream() << "x=" << x).str()
curiousguy
@curiousguy: Sim, esse é o conteúdo do link que postei.
Kerrek SB

Respostas:

132

C ++ 03 3.10 / 1 diz: "Cada expressão é um lvalue ou um rvalue." É importante lembrar que valor versus valor valor é uma propriedade de expressões, não de objetos.

Lvalues ​​nomeia objetos que persistem além de uma única expressão. Por exemplo, obj, *ptr, ptr[index], e ++xsão todos lvalues.

Os valores R são temporários que evaporam no final da expressão completa em que vivem ("no ponto e vírgula"). Por exemplo, 1729, x + y, std::string("meow"), e x++são todos rvalues.

O operador address-of requer que seu "operando deve ser um lvalue". se pudéssemos pegar o endereço de uma expressão, a expressão seria um lvalue, caso contrário, seria um rvalue.

 &obj; //  valid
 &12;  //invalid
BruceAdi
fonte
2
" se pudéssemos pegar o endereço de uma expressão, a expressão é um lvalue, caso contrário, é um rvalue. " Se apenas C ++ fosse tão simples! (mas a nuance não é realmente relevante aqui) "Rvalues ​​são temporários" temporários o quê? objetos?
curioso
2
@curiousguyRvalues ​​são temporários que desaparecem no final da expressão plena em que vivem. Para simplesmente responder por que express int & = 12;é inválido, o padrão diz que um literal de string é um lvalue, outros literais são rvalues.
BruceAdi
@curiousguy: Sim. Rvalues ​​são objetos temporários , mas nem todos os rvalues ​​são objetos temporários; alguns nem são objetos.
Nawaz
" Por exemplo, 1729, x + y, std :: string (" meow ") e x ++ são todos rvalues. " Mas std::string("meow")constrói um objeto do tipo std::stringe produz um rvalue que designa este objeto, 1729não tem efeito colateral e produz valor 1729 como um rvalue do tipo int.
curiousguy
1
@curiousguy: A afirmação "Lvalues name objects that persist beyond a single expression."é 100% correta. Pelo contrário, seu exemplo (const int &)1está incorreto, porque NÃO é um objeto "nomeado".
Nawaz
53
int &z = 12;

No lado direito, um objeto temporário do tipo inté criado a partir do literal integral 12, mas o temporário não pode ser vinculado a uma referência não const. Daí o erro. É o mesmo que:

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

Por que um temporário é criado? Porque uma referência deve se referir a um objeto na memória, e para um objeto existir, ele deve ser criado primeiro. Como o objeto não tem nome, ele é um objeto temporário . Não tem nome. A partir dessa explicação, ficou bastante claro por que o segundo caso está bom.

Um objeto temporário pode ser vinculado à referência const, o que significa que você pode fazer o seguinte:

const int &z = 12; //ok

Referência C ++ 11 e Rvalue:

Para fins de integridade, gostaria de acrescentar que o C ++ 11 introduziu rvalue-reference, que pode vincular a um objeto temporário. Portanto, em C ++ 11, você pode escrever isto:

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

Observe que existe em vez &&de &. Observe também que constnão é mais necessário, embora o objeto zvinculado seja um objeto temporário criado a partir de integral literal 12.

Como o C ++ 11 introduziu rvalue-reference , int&agora é chamado de lvalue-reference .

Nawaz
fonte
10

12é uma constante de tempo de compilação que não pode ser alterada ao contrário dos dados referenciados por int&. O que você pode fazer é

const int& z = 12;
Michael Krelin - hacker
fonte
2
@curiousguy, seja consistente, você disse "Você tem que entender que essas são regras C ++. Elas existem e não precisam de qualquer justificativa" (para não mencionar a primeira edição). Como devo interpretar sua reclamação de não dar o que é de acordo com você não é necessária?
Michael Krelin - hacker de
2
@ MichaelKrelin-hacker: Tecnicamente não, você não pode (nunca) vincular uma referência a um valor (ou constante de tempo de compilação), o padrão é bastante explícito quanto ao que realmente acontece: Caso contrário, um temporário do tipo “cv1 T1” é criado e inicializado a partir da expressão do inicializador usando as regras para uma inicialização de cópia sem referência (8.5). A referência é então ligada ao temporário. Isto é, a sintaxe é permitida, mas a semântica não é aquela de ligar uma referência à constante, mas sim ligá-la ao temporário que é implicitamente criado.
David Rodríguez - dribeas
1
@curiousguy: As regras da linguagem fazem parte do design e, na maioria das vezes, existem justificativas para o porquê da linguagem ter sido desenvolvida dessa forma. Neste caso particular, como em C, você não tem permissão para obter o endereço de um valor (não há um), nem pode vincular uma referência. Agora considere uma função void f( vector<int> const & )idiomática para passar um vetor que não deve ser modificado. O problema agora é que isso f( vector<int>(5) )seria incorreto e o usuário teria que fornecer uma sobrecarga diferente, o void f( vector<int> v ) { f(v); }que é trivial.
David Rodríguez - dribeas
1
... agora porque isso seria doloroso para os usuários da linguagem, os designers decidiram que o compilador realizaria a operação equivalente para você, na chamada f( vector<int>(5) ), o compilador cria um temporário e então vincula a referência àquele temporário, e da mesma forma se houve uma conversão implícita de 5diretamente. Isso permite que o compilador gere uma única assinatura para a função e permite uma única implementação da função pelo usuário. A partir daí, um comportamento semelhante é definido para o restante dos usos de referências constantes para consistência.
David Rodríguez - dribeas
1
@ MichaelKrelin-hacker: as referências são apelidos para objetos e um valor não é um objeto. Dependendo do contexto, as referências podem ser apenas apelidos, o compilador remove a referência e apenas usa o identificador para significar o que quer que o objeto original significasse ( T const & r = *ptr;qualquer uso posterior de rna função pode ser substituído por *ptre rnão precisa existir em tempo de execução) ou pode ter que ser implementado mantendo o endereço do objeto que ele atribui (considere armazenar uma referência como um membro de um objeto) - que é implementado como um ponteiro autodereferenciado.
David Rodríguez - dribeas
4

Vinculação de referência não const e const seguem regras diferentes

Estas são as regras da linguagem C ++:

  • uma expressão que consiste em um número literal ( 12) é um "rvalue"
  • não é permitido criar uma referência não const com um rvalue: int &ri = 12;está malformado
  • é permitido criar uma referência const com um rvalue: neste caso, um objeto não nomeado é criado pelo compilador; este objeto persistirá enquanto a própria referência existir.

Você tem que entender que essas são regras C ++. Eles simplesmente são.

É fácil inventar uma linguagem diferente, digamos C ++ ', com regras ligeiramente diferentes. Em C ++ ', seria permitido criar uma referência não const com um rvalue. Não há nada inconsistente ou impossível aqui.

Mas isso permitiria algum código arriscado onde o programador poderia não obter o que pretendia, e os designers de C ++ decidiram corretamente evitar esse risco.

cara curioso
fonte
0

As referências são "ponteiros ocultos" (não nulos) para coisas que podem mudar (lvalores). Você não pode defini-los como uma constante. Deve ser uma coisa "variável".

EDITAR::

Estou pensando em

int &x = y;

quase equivalente a

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

onde __pxé um nome novo, e as #define xobras apenas dentro do bloco que contém a declaração de xreferência.

Basile Starynkevitch
fonte
1
Por que você pode, se a referência for const:)
Michael Krelin - hacker
Mas o exemplo do pôster não foiconst
Basile Starynkevitch
Sim, eu quis dizer que sua frase "referências são indicadores de coisas que podem mudar" - é sobre referências sem custo.
Michael Krelin - hacker de
" As referências são 'ponteiros escondido' " erradas " para coisas que podem mudar " erradas " coisas que podem mudar (lvalues). " Errado
curiousguy
@Loki: você poderia explicar mais?
Basile Starynkevitch