den utrolige Const Reference, der ikke er Const
mens jeg arbejdede på NamedType-biblioteket, stødte jeg på en situation, der efterlod mig bedøvet i forvirring: en const-reference, der tillader ændring af det objekt, det refererer til. Uden en const_cast
. Uden en mutable
. Uden noget i ærmet.
Hvordan kan det være? Og hvordan håndhæves const
i den const-reference?
en const reference, der ikke er const
her er en beskrivelse af en situation, hvor denne mærkelige reference kommer op. Overvej følgende indpakningsklasse:
template<typename T>class Wrapper{public: Wrapper(T const& value) : value_(value) {} T const& get() const { return value_; } private: T value_;};
denne Wrapper
klasse gemmer en værdi af typen T
og giver adgang til den via get()
– metoden (uafhængigt af det, hvis du spekulerer på, om “get” er et godt navn, kan du læse denne detaljerede diskussion). Den vigtige del er, at get()
– metoden kun giver en skrivebeskyttet adgang til den lagrede værdi, fordi den returnerer en const-reference.
for at kontrollere det, lad os instantiere skabelonen med int
, for eksempel. Forsøg på at ændre den lagrede værdi gennem get()
– metoden undlader at kompilere (prøv at klikke på knappen Kør nedenfor).
hvilket er helt fint, fordi vi ønsker get()
at være skrivebeskyttet.
men lad os nu instantiere Wrapper
med int&
. En brugssag til dette kan være at undgå en kopi eller holde styr på potentielle ændringer af a
for eksempel. Lad os nu prøve at ændre den lagrede værdi gennem const-referencen returneret af get()
:
og hvis du rammer KØREKNAPPEN, vil du se det… det kompilerer! Og det udskriver den værdi, der er ændret gennem const-referencen, som om der ikke var sket noget særligt.
er det ikke forvirrende? Føler du ikke det beroligende indtryk af sikkerhed, som const
kollapser omkring os? Hvem kan vi stole på?
lad os prøve at forstå, hvad der foregår med denne reference.
Const reference eller henvisning til const?
kernen i sagen er, at vores a-reference er en const-reference, men ikke en henvisning til const.
til undersog hvad det betyder, Lad os nedbryde trin for trin, hvad der sker i skabeloninstantiationen.
metoden get()
returnerer en const T&
, hvor T
kommer fra template T
. I vores andet tilfælde er T
int&
, så const T&
er const (int&) &
.
lad os se nærmere på dette const (int&)
. int&
er en reference, der henviser til en int
, og har lov til at ændre den int
. Men const (int&)
er en reference int&
det er const
, hvilket betyder, at referencen selv ikke kan ændres.
Hvad betyder det at ændre en reference til at begynde med? I teorien ville det betyde at få det til at henvise til et andet objekt eller ikke henvise til noget. Men begge disse muligheder er ulovlige i C++: en reference kan ikke rebind. Det refererer til det samme objekt i hele løbet af det liv.
så at være const
siger ikke meget til en reference, da de altid er const
, da de ikke kan rebind. Dette indebærer, at const (int&)
faktisk er den samme type som int&
.
hvilket efterlader os med get()
vender tilbage (int&) &
. Og efter reglerne for referencer, der kollapser, er dette int&
.
så get
returnerer en int&
. Dens grænseflade siger T const&
, men det er faktisk en int&
. Ikke rigtig udtryksfuld kode, er det?
Hvordan laver const-referencen en henvisning til const
nu hvor vi er forbi det første skridt til at forstå problemet, Hvordan kan vi løse det? Det vil sige, hvordan kan vi få get()
til at returnere en henvisning til den lagrede værdi, der ikke tillader at ændre den?
en måde at gøre dette på er at eksplicit indsætte en const
inde i referencen. Vi kan gøre dette ved at fjerne referencen, tilføje en const
og sætte referencen tilbage. For at fjerne referencen bruger vi std::remove_reference
i C++11, eller den mere bekvemme std::remove_reference_t
i C++14:
som du kan se, om du klikker på Kør, vises den forventede kompileringsfejl nu, når du prøver at ændre den værdi, der er gemt i Wrapper
.
jeg synes, det er nyttigt at forstå, hvad der foregår med denne reference, fordi dette er kode, der ser ud som om det er sikkert, men det er virkelig ikke, Og det er den farligste slags.
du kan lege med koden i ovenstående legepladser indlejret på siden. Og en stor tak til stack overløb bruger rakete1111 for at hjælpe mig med at forstå årsagerne til dette problem.
du kan også lide
- den mest irriterende Parse: Sådan finder du det og retter det hurtigt