Den Utrolige Const Referansen Som Ikke Er Const
mens jeg jobbet På NamedType-biblioteket, kom jeg over en situasjon som gjorde meg bedøvet i forvirring: en const-referanse som tillater endring av objektet det refererer til. Uten en const_cast
. Uten en mutable
. Uten noe opp i ermet.
Hvordan kan dette være? Og hvordan å håndheve const
i den const referansen?
en const-referanse som ikke er const
her er en beskrivelse av en situasjon der denne merkelige referansen kommer opp. Vurder følgende wrapper klasse:
template<typename T>class Wrapper{public: Wrapper(T const& value) : value_(value) {} T const& get() const { return value_; } private: T value_;};
denne klassen Wrapper
lagrer en verdi av typen T
, og gir tilgang til den via metoden get()
(uavhengig av det, hvis du lurer på om “get” er et godt navn, vil du kanskje lese denne detaljerte diskusjonen). Den viktige delen er at metoden get()
bare gir skrivebeskyttet tilgang til den lagrede verdien, fordi den returnerer en const-referanse.
for å sjekke det, la oss starte malen med int
, for eksempel. Forsøk på å endre den lagrede verdien gjennom metoden get()
mislykkes i å kompilere (prøv Å klikke På Kjør-knappen nedenfor).
Det er bare bra, fordi vi vil at get()
skal være skrivebeskyttet.
Men la oss nå instantiere Wrapper
med int&
. En brukstilfelle for dette kan være å unngå en kopi, eller holde oversikt over potensielle modifikasjoner av a
for eksempel. La oss nå prøve å endre den lagrede verdien gjennom const-referansen returnert av get()
:
og hvis du treffer RUN-knappen vil du se at … det kompilerer! Og det skriver ut verdien endret gjennom const referansen, som ikke noe spesielt hadde skjedd.
Er ikke dette forvirrende? Føler du ikke det betryggende inntrykket av sikkerhet som const
kollapser rundt oss? Hvem kan vi stole på?
La oss prøve å forstå hva som skjer med denne referansen.
Const referanse eller referanse til const?
sakens kjerne er at vår referanse er en const-referanse, men ikke en referanse til const.
for å undersand hva det betyr, la oss dekomponere trinnvis hva som skjer i malinstanseringen.
metoden get()
returnerer en const T&
, med T
kommer fra template T
. I vårt andre tilfelle er T
int&
, så const T&
er const (int&) &
.
La oss se nærmere på dette const (int&)
. int&
er en referanse som refererer til en int
, og kan endre den int
. Men const (int&)
er en referanse int&
som er const
, noe som betyr at referansen selv ikke kan endres.
Hva betyr det å endre en referanse til å begynne med? I teorien vil det bety at det refererer til et annet objekt, eller ikke refererer til noe. Men begge disse mulighetene er ulovlige I C++: en referanse kan ikke rebind. Det refererer til det samme objektet i hele løpet av det livet.
så å være const
sier ikke mye for en referanse, siden de alltid er const
, siden de ikke kan rebind. Dette innebærer at const (int&)
er effektivt den samme typen som int&
.
som etterlater oss med get()
retur (int&) &
. Og etter reglene for referanser kollapser, er dette int&
.
Så get
returnerer en int&
. Grensesnittet sier T const&
, men det er faktisk en int&
. Ikke egentlig uttrykksfull kode, er det?
hvordan lage const referansen en referanse til const
Nå som vi er forbi det første trinnet for å forstå problemet, hvordan kan vi fikse det? Det er, hvordan kan vi lage get()
returnere en referanse til den lagrede verdien som ikke tillater å endre den?
en måte å gjøre dette på er å eksplisitt sette inn en const
inne i referansen. Vi kan gjøre dette ved å strippe av referansen, legge til en const
, og sette referansen tilbake. For å fjerne referansen bruker vi std::remove_reference
I C++11 ,eller den mer praktiske std::remove_reference_t
I C++14:
Som du kan se om du klikker På Kjør, vises den forventede kompileringsfeilen nå når du prøver å endre verdien som er lagret i Wrapper
.
jeg synes det er nyttig å forstå hva som skjer med denne referansen, fordi dette er kode som ser ut som det er trygt, men er egentlig ikke, og dette er den farligste typen.
du kan leke deg med koden på de ovennevnte lekeplassene som er innebygd på siden. Og en stor takk til stack overflow bruker rakete1111 for å hjelpe meg å forstå årsakene bak dette problemet.
du kan også like
- Den Mest Vexing Parse: Hvordan Å Få Øye På Det og Fikse Det Raskt