incredibilul Const referință care nu este Const
în timp ce lucram la Biblioteca NamedType am dat peste o situație care m-a lăsat uimit de nedumerire: o referință const care permite modificarea obiectului la care se referă. Fără const_cast
. Fără mutable
. Fără nimic în mânecă.
cum poate fi aceasta? Și cum să impunem const
în acea referință const?
o referință const care nu este const
Iată o descriere a unei situații în care apare această referință ciudată. Luați în considerare următoarea clasă de înveliș:
template<typename T>class Wrapper{public: Wrapper(T const& value) : value_(value) {} T const& get() const { return value_; } private: T value_;};
această clasă Wrapper
stochează o valoare de tip T
și oferă acces la aceasta prin metoda get()
(independent de aceasta, dacă vă întrebați dacă “get” este un nume bun, poate doriți să citiți această discuție detaliată). Partea importantă este că metoda get()
oferă doar un acces numai în citire la valoarea stocată, deoarece returnează o referință const.
pentru a verifica acest lucru, să instanțiem șablonul cu int
, de exemplu. Încercarea de a modifica valoarea stocată prin metoda get()
nu reușește să compileze (încercați să faceți clic pe butonul Run de mai jos).
ceea ce este foarte bine, pentru că vrem ca get()
să fie doar în citire.
dar să instanțiem acum Wrapper
cu int&
. Un caz de utilizare pentru acest lucru ar putea fi evitarea unei copii sau urmărirea modificărilor potențiale ale a
de exemplu. Acum să încercăm să modificăm valoarea stocată prin referința const returnată de get()
:
și dacă te-a lovit pe termen buttong veți vedea că … compilează! Și imprimă valoarea modificată prin referința const, ca și cum nu s-ar fi întâmplat nimic special.
nu este aceasta derutant? Nu simți impresia liniștitoare de siguranță oferită de const
prăbușindu-se în jurul nostru? În cine putem avea încredere?
să încercăm să înțelegem ce se întâmplă cu această referință.
Const referință sau referință la const?
esența problemei este că referința noastră a este o referință const, dar nu o referință la const.
pentru a undersand ce înseamnă asta, să descompunem pas cu pas ceea ce se întâmplă în instanțierea șablonului.
metoda get()
returnează un const T&
, cu T
provenind de la template T
. În al doilea caz, T
este int&
, deci const T&
este const (int&) &
.
să aruncăm o privire mai atentă la acest const (int&)
. int&
este o referință care se referă la un int
și este permisă modificarea acelui int
. Dar const (int&)
este o referință int&
care este const
, ceea ce înseamnă că referința în sine nu poate fi modificată.
ce înseamnă să modifici o referință, pentru început? În teorie, ar însemna să-l facem să se refere la un alt obiect sau să nu se refere la nimic. Dar ambele aceste posibilități sunt ilegale în c++: o referință nu poate rebind. Se referă la același obiect pe tot parcursul vieții sale.
deci a fi const
nu spune prea multe pentru o referință, deoarece întotdeauna sunt const
, deoarece nu se pot rebind. Aceasta implică faptul că const (int&)
este efectiv același tip ca int&
.
ceea ce ne lasă cu get()
revenind (int&) &
. Și după regulile referințelor care se prăbușesc, acesta este int&
.
deci get
returnează un int&
. Interfața sa spune T const&
, dar este de fapt un int&
. Nu e un cod expresiv, nu?
cum se face referința const o referință la const
acum că am trecut de primul pas de înțelegere a problemei, cum o putem remedia? Adică, cum putem face get()
să returneze o referință la valoarea stocată care nu permite modificarea acesteia?
o modalitate de a face acest lucru este de a introduce în mod explicit un const
în interiorul referinței. Putem face acest lucru prin eliminarea referinței, adăugând un const
și punând referința înapoi. Pentru a elimina referința, folosim std::remove_reference
în C++11 sau mai convenabil std::remove_reference_t
în C++14:
după cum puteți vedea dacă faceți clic pe Executare, eroarea de compilare așteptată apare acum când încercați să modificați valoarea stocată în Wrapper
.
cred că este util să înțelegem ce se întâmplă cu această referință, deoarece acesta este un cod care pare că este sigur, dar nu este într-adevăr, iar acesta este cel mai periculos tip.
puteți juca cu codul din locurile de joacă de mai sus încorporate în pagină. Și o mare mulțumire utilizatorului Stack overflow rakete1111 pentru că m-a ajutat să înțeleg motivele din spatele acestei probleme.
ați putea dori, de asemenea,
- analiza cea mai supărătoare: cum să-l fața locului și repara rapid