Neuvěřitelné Const Odkaz, Který Není Const
Při práci na NamedType knihovny jsem narazil na situaci, že mě opustil omráčený v úžasu: const odkaz, který umožňuje modifikaci objekt odkazuje. Bez const_cast
. Bez mutable
. Bez čehokoli v rukávu.
jak to může být? A jak prosadit const
v této konstelaci?
odkaz const, který není const
zde je popis situace, kdy se tento podivný odkaz objeví. Zvažte následující třídy obálky:
template<typename T>class Wrapper{public: Wrapper(T const& value) : value_(value) {} T const& get() const { return value_; } private: T value_;};
Wrapper
třída uchovává hodnotu typu T
, a dává k němu přístup přes get()
metoda (nezávisle od toho, jestli tě zajímá, zda “se” je dobrý název, možná budete chtít přečíst tento podrobnější diskusi). Důležité je, že metoda get()
poskytuje přístup k uložené hodnotě pouze pro čtení, protože vrací odkaz const.
Chcete-li to zkontrolovat, instantujme šablonu například pomocí int
. Pokus o změnu uložené hodnoty pomocí metody get()
se nepodaří zkompilovat (zkuste kliknout na tlačítko Spustit níže).
což je v pořádku, protože chceme, aby get()
bylo jen pro čtení.
ale pojďme nyní instanci Wrapper
s int&
. Jedním z případů použití by mohlo být například vyhnout se kopii nebo sledovat možné úpravy a
. Nyní se pokusíme upravit uloženou hodnotu pomocí odkazu const vráceného get()
:
a pokud narazíte na běh button uvidíte, že … to kompiluje! A vytiskne hodnotu upravenou pomocí konstantního odkazu, jako by se nic zvláštního nestalo.
není to matoucí? Necítíte uklidňující dojem bezpečí, který poskytuje const
kolaps kolem nás? Komu můžeme věřit?
pokusme se pochopit, co se děje s tímto odkazem.
Const odkaz nebo odkaz na const?
podstatou věci je, že náš odkaz a je odkazem na const, ale ne odkazem na const.
Chcete-li PODA co to znamená, rozložte krok za krokem to, co se děje v instanci šablony.
metoda get()
vrací const T&
, přičemž T
pochází z template T
. V našem druhém případě je T
int&
, takže const T&
je const (int&) &
.
podívejme se blíže na toto const (int&)
. int&
je odkaz, který odkazuje na int
, a je oprávněn upravit int
. Ale const (int&)
je odkaz int&
, který je const
, což znamená, že samotný odkaz nelze změnit.
co to znamená změnit odkaz, začít? Teoreticky by to znamenalo, že by odkazoval na jiný objekt nebo na nic neodkazoval. Ale obě tyto možnosti jsou v C++ nezákonné: odkaz se nemůže znovu připojit. Vztahuje se na stejný objekt po celou dobu jeho života.
takže být const
neříká mnoho pro referenci, protože jsou vždy const
, protože se nemohou znovu spojit. To znamená, že const (int&)
je v podstatě stejný typ jako int&
.
což nám ponechává get()
vracející se (int&) &
. A podle pravidel referencí je to int&
.
takže get
vrací int&
. Jeho rozhraní říká T const&
, ale ve skutečnosti je to int&
. Není to moc výrazný kód, že?
jak učinit z odkazu const odkaz na const
Nyní, když jsme za prvním krokem pochopení problému, jak to můžeme opravit? To znamená, jak můžeme get()
vrátit odkaz na uloženou hodnotu, která neumožňuje její úpravu?
jedním ze způsobů, jak toho dosáhnout, je explicitně vložit const
do odkazu. Můžeme to udělat odstraněním odkazu, přidáním const
a vložením odkazu zpět. Svléknout odkaz, můžeme použít std::remove_reference
v C++11, nebo pohodlnější std::remove_reference_t
v C++14:
Jak můžete vidět, pokud kliknete na Spustit, očekávané kompilace chyby se zobrazí, když se snaží změnit hodnotu uloženou v Wrapper
.
myslím, že je užitečné pochopit, co se děje s tímto odkazem, protože to je kód, který vypadá, že je Bezpečný, ale ve skutečnosti není, a to je nejnebezpečnější druh.
můžete si pohrát s kódem na výše uvedených hřištích vložených do stránky. A velké díky uživateli Stack overflow rakete1111 za to, že mi pomohl pochopit důvody tohoto problému.
mohlo by se vám také líbit
- nejvíce znepokojující analýza: jak ji najít a rychle opravit