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.
" 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:
constint &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 .
@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.
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.
(ostringstream() << "x=" << x).str()
Respostas:
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++x
sã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")
, ex++
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
fonte
int & = 12;
é inválido, o padrão diz que um literal de string é um lvalue, outros literais são rvalues.std::string("meow")
constrói um objeto do tipostd::string
e produz um rvalue que designa este objeto,1729
não tem efeito colateral e produz valor 1729 como um rvalue do tipoint
."Lvalues name objects that persist beyond a single expression."
é 100% correta. Pelo contrário, seu exemplo(const int &)1
está incorreto, porque NÃO é um objeto "nomeado".int &z = 12;
No lado direito, um objeto temporário do tipo
int
é criado a partir do literal integral12
, 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 queconst
não é mais necessário, embora o objetoz
vinculado seja um objeto temporário criado a partir de integral literal12
.Como o C ++ 11 introduziu rvalue-reference ,
int&
agora é chamado de lvalue-reference .fonte
12
é uma constante de tempo de compilação que não pode ser alterada ao contrário dos dados referenciados porint&
. O que você pode fazer éconst int& z = 12;
fonte
void f( vector<int> const & )
idiomática para passar um vetor que não deve ser modificado. O problema agora é que issof( vector<int>(5) )
seria incorreto e o usuário teria que fornecer uma sobrecarga diferente, ovoid f( vector<int> v ) { f(v); }
que é trivial.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 de5
diretamente. 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.T const & r = *ptr;
qualquer uso posterior der
na função pode ser substituído por*ptr
er
nã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.Vinculação de referência não const e const seguem regras diferentes
Estas são as regras da linguagem C ++:
12
) é um "rvalue"int &ri = 12;
está malformadoVocê 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.
fonte
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 x
obras apenas dentro do bloco que contém a declaração dex
referência.fonte
const
:)const