błąd: niepoprawna inicjalizacja odwołania innego niż const typu „int &” z wartości r typu „int”

87

Błędny formularz:

int &z = 12;

Poprawna forma:

int y;
int &r = y;

Pytanie :
Dlaczego pierwszy kod jest nieprawidłowy? Jakie jest „ znaczenie ” błędu w tytule?

Aquarius_Girl
źródło
4
Tymczasowe nie mogą być powiązane z niestałymi odwołaniami. int (12) jest w tym przypadku tymczasowym.
Prasoon Saurav
@PrasoonSaurav Co masz na myśli mówiąc o tymczasowej 12? Brak koncepcji tutaj (z mojej strony :))
Aquarius_Girl
2
Zauważ, że nie ma ścisłego technicznego powodu tego ograniczenia. Równie łatwo byłoby zaimplementować zezwolenie na zmienne odniesienia do tymczasowych. Zakazanie tego jest decyzją projektową C ++, ponieważ taka konstrukcja byłaby kiepskim projektem o znacznie większym ryzyku nieumyślnego nadużycia niż autentyczna użyteczność. (Tylko raz znalazłem wymyśloną potrzebę czegoś takiego.)
Kerrek SB
@KerrekSB prawdopodobnie najczęstszą potrzebą powiązania odniesienia do obiektu rvalue jest(ostringstream() << "x=" << x).str()
curiousguy.
@curiousguy: Tak, to jest treść linku, który zamieściłem.
Kerrek SB

Odpowiedzi:

132

C ++ 03 3.10 / 1 mówi: „Każde wyrażenie jest albo lwartością, albo rwartością”. Należy pamiętać, że wartość kontra wartość jest właściwością wyrażeń, a nie przedmiotów.

Lvalues ​​nazywają obiekty, które pozostają poza jednym wyrażeniem. Na przykład obj, *ptr, ptr[index], i ++xsą lwartościami.

Wartości R to wartości tymczasowe, które wyparowują na końcu pełnego wyrażenia, w którym żyją („w średniku”). Na przykład 1729, x + y, std::string("meow"), i x++są rvalues.

Operator address-of wymaga, aby jego „operand był l-wartością”. gdybyśmy mogli wziąć adres jednego wyrażenia, wyrażenie jest lwartością, w przeciwnym razie jest to rwartość.

 &obj; //  valid
 &12;  //invalid
BruceAdi
źródło
2
gdybyśmy mogli wziąć adres jednego wyrażenia, wyrażenie jest lwartością, w przeciwnym razie jest to wartość r. ” gdyby tylko C ++ był taki prosty! (ale ten niuans nie jest tutaj zbyt istotny) „Wartości R są tymczasowe” co tymczasowe? przedmioty?
curiousguy
2
Wartości @curiousguyR to wartości tymczasowe, które znikają pod koniec pełnego wyrażenia, w którym żyją. Aby po prostu odpowiedzieć, dlaczego wyrażenie int & = 12;jest nieprawidłowe, standard mówi, że literał łańcuchowy to lwartość, a inne literały to rwartości.
BruceAdi
@curiousguy: Tak. Wartości r są obiektami tymczasowymi , ale nie wszystkie wartości r są obiektami tymczasowymi; niektóre nie są nawet przedmiotami.
Nawaz
Na przykład 1729 x + y, std :: łańcucha (” meow „), a x ++ są rvalues. ”, Lecz std::string("meow")tworzy się obiekt typu std::stringi daje RValue że wyznacza tego celu, 1729nie ma działań ubocznych i plony wartości 1729 jako rwartość typu int.
curiousguy
1
@curiousguy: To stwierdzenie "Lvalues name objects that persist beyond a single expression."jest w 100% poprawne. Wręcz przeciwnie, twój przykład (const int &)1jest niepoprawny, ponieważ NIE jest „nazwanym” obiektem.
Nawaz
53
int &z = 12;

Po prawej stronie tymczasowy obiekt typu intjest tworzony z integralnego literału 12, ale tymczasowy nie może być powiązany z odwołaniem innym niż const. Stąd błąd. To jest to samo, co:

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

Dlaczego tworzy się tymczasowy? Ponieważ odniesienie musi odnosić się do obiektu w pamięci, a aby obiekt istniał, musi zostać najpierw utworzony. Ponieważ obiekt nie ma nazwy, jest to obiekt tymczasowy . Nie ma imienia. Z tego wyjaśnienia stało się dość jasne, dlaczego drugi przypadek jest w porządku.

Obiekt tymczasowy można powiązać z odniesieniem do const, co oznacza, że ​​możesz to zrobić:

const int &z = 12; //ok

C ++ 11 i Rvalue Reference:

Ze względu na kompletność chciałbym dodać, że C ++ 11 wprowadził rvalue-reference, które można powiązać z tymczasowym obiektem. Więc w C ++ 11 możesz napisać to:

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

Zauważ, że jest &&intead of &. Zauważ również, że constnie jest już potrzebne, mimo że obiekt, z którym się złączy, jest tymczasowym obiektem utworzonym z całki-literału 12.

Ponieważ C ++ 11 wprowadził odwołanie do wartości r , int&odtąd nazywane jest odniesieniem do wartości .

Nawaz
źródło
10

12jest stałą czasu kompilacji, której nie można zmienić w przeciwieństwie do danych, do których się odwołuje int&. Możesz to zrobić

const int& z = 12;
Michael Krelin - haker
źródło
2
@curiousguy, bądź konsekwentny, powiedziałeś "Musisz zrozumieć, że to są reguły C ++. One istnieją i nie potrzebują żadnego uzasadnienia" (nie wspominając o pierwszym wydaniu). Jak mam zinterpretować twoje narzekanie, że nie dajesz tego, co według ciebie nie jest potrzebne?
Michael Krelin - haker
2
@ MichaelKrelin-hacker: Technicznie nie, nie możesz (nigdy) powiązać odniesienia z wartością (lub stałą czasową kompilacji), standard jest dość wyraźny co do tego, co się dzieje: w przeciwnym razie tworzony jest tymczasowy typ „cv1 T1” i zainicjowany z wyrażenia inicjatora przy użyciu reguł inicjowania kopii bez odniesienia (8.5). Odniesienie jest następnie powiązane z tymczasowym. Oznacza to, że składnia jest dozwolona, ​​ale semantyka nie polega na wiązaniu odniesienia do stałej, ale raczej na wiązaniu go z tymczasowym, który jest domyślnie utworzony.
David Rodríguez - dribeas
1
@curiousguy: Reguły języka są częścią projektu i najczęściej istnieją uzasadnienia, dlaczego język został zaprojektowany w ten sposób. W tym szczególnym przypadku, tak jak w C, nie możesz wziąć adresu wartości (ona go nie ma) ani powiązać odwołania. Rozważmy teraz funkcję void f( vector<int> const & ), która jest idiomatyczna, aby przekazać wektor, który nie ma być modyfikowany. Problem polega teraz na tym f( vector<int>(5) ), że byłoby to niepoprawne, a użytkownik musiałby podać inne przeciążenie, void f( vector<int> v ) { f(v); }które jest trywialne.
David Rodríguez - dribeas
1
... teraz, ponieważ byłoby to bolesne dla użytkowników języka, projektanci zdecydowali, że kompilator wykona za Ciebie równoważną operację, w wywołaniu f( vector<int>(5) )kompilator utworzy tymczasową, a następnie wiąże odwołanie z tym tymczasowym i podobnie jeśli wystąpiła niejawna konwersja z 5bezpośrednio. Pozwala to kompilatorowi na wygenerowanie pojedynczego podpisu dla funkcji i umożliwia implementację funkcji przez jednego użytkownika. Od tego momentu podobne zachowanie jest definiowane dla pozostałych zastosowań stałych odniesień dla spójności.
David Rodríguez - dribeas
1
@ MichaelKrelin-hacker: odwołania to aliasy obiektów, a wartość nie jest obiektem. W zależności od kontekstu, odwołania mogą być po prostu aliasami, kompilator usuwa odniesienie i po prostu używa identyfikatora do oznaczenia cokolwiek oznaczał oryginalny obiekt ( T const & r = *ptr;każde późniejsze użycie rw funkcji może zostać zastąpione przez *ptri rnie musi istnieć w czasie wykonywania) lub może być zaimplementowane poprzez zachowanie adresu obiektu, do którego przypisuje aliasy (rozważ przechowywanie referencji jako członka obiektu) - który jest implementowany jako wskaźnik z autodreferencją.
David Rodríguez - dribeas
4

Wiązanie odniesienia inne niż stałe i stałe podlega innym regułom

Oto zasady języka C ++:

  • wyrażenie składające się z literału liczby ( 12) jest „wartością r”
  • nie jest dozwolone tworzenie referencji innych niż const z rvalue: int &ri = 12;jest źle sformułowana
  • dozwolone jest tworzenie odwołania do const z wartością r: w tym przypadku nienazwany obiekt jest tworzony przez kompilator; ten obiekt będzie istniał tak długo, jak długo istnieje odwołanie.

Musisz zrozumieć, że to są reguły C ++. Po prostu są.

Łatwo jest wymyślić inny język, na przykład C ++, z nieco innymi zasadami. W C ++ byłoby dozwolone utworzenie referencji niebędącej stałą z wartością r. Nie ma tu nic niespójnego ani niemożliwego.

Ale pozwoliłoby to na pewien ryzykowny kod, w którym programista mógłby nie uzyskać tego, co zamierzał, a projektanci C ++ słusznie postanowili uniknąć tego ryzyka.

ciekawy facet
źródło
0

Odniesienia to „ukryte wskaźniki” (niezerowe) do rzeczy, które mogą się zmieniać (lvalues). Nie możesz ich zdefiniować jako stałe. Powinna to być rzecz „zmienna”.

EDYTOWAĆ::

Myślę o

int &x = y;

jako prawie odpowiednik

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

gdzie __pxjest nową nazwą, a #define xdziała tylko wewnątrz bloku zawierającego deklarację xodniesienia.

Basile Starynkevitch
źródło
1
Dlaczego możesz, jeśli odniesienie to const:)
Michael Krelin - haker
Ale przykład plakatu nie byłconst
Basile Starynkevitch
Tak, chodziło mi o twoje sformułowanie „odniesienia to wskazówki do rzeczy, które mogą się zmienić” - chodzi o odniesienia niezwiązane z kosztami.
Michael Krelin - haker
Odniesienia są«ukryte»wskaźniki ” złe „ rzeczy, które mogą zmienić ” źle „ rzeczy, które mogą się zmieniać (lwartościami). ” Niewłaściwy
curiousguy
@Loki: czy mógłbyś wyjaśnić więcej?
Basile Starynkevitch