Menu

Słowo kluczowe auto w C++: dedukcja typu na przykładach

Jak słowo kluczowe auto pozwala kompilatorowi wywnioskować typ zmiennej za ciebie: co odrzuca, gdzie się sprawdza i jakie pułapki czekają na początkujących.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Niech kompilator wybierze typ

Na poprzedniej stronie widać było, że każda zmienna w C++ ma stały typ: int, double, std::string i tak dalej. Ręczne wypisanie typu jest w porządku przy int count = 0;, ale robi się uciążliwe, gdy typy stają się długie. Słowo kluczowe auto (od C++11) pozwala kompilatorowi wywnioskować typ z przypisywanej wartości, więc piszesz wartość raz, a resztę uzupełnia kompilator.

Kluczowa myśl: auto to nie dynamiczne typowanie. Każda zmienna nadal ma jeden konkretny typ, ustalony w czasie kompilacji. auto oszczędza ci tylko jego wypisywania.

Dlaczego auto się opłaca

Przy krótkich, oczywistych typach auto count = 0; i int count = 0; są równie czytelne. auto naprawdę się opłaca przy długich, powtarzalnych nazwach typów, takich jak te z kontenerów biblioteki standardowej i iteratorów.

Porównaj rozwlekłą wersję z wersją z auto:

// Bez auto: typ w praktyce jest zapisany dwa razy
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();

// Z auto: kompilator już zna typ
auto it = scores.begin();

Obie deklarują dokładnie ten sam typ iteratora. Druga jest czytelniejsza i nie rozjedzie się z resztą kodu, jeśli później zmienisz scores na inny kontener.

Oto to samo w kompletnym programie:

auto w pętlach for po zakresie

Najczęściej spotkasz auto w pętli for po zakresie. Prawie nigdy nie chcesz ręcznie wypisywać typu elementu, a sposób zapisania auto decyduje, czy dostajesz kopię, czy referencję.

Trzy warianty, które zobaczysz, i co każdy oznacza:

  • for (auto x : v): x jest kopią każdego elementu. Tanie dla int, marnotrawne dla dużych obiektów.
  • for (auto& x : v): x jest referencją; możesz modyfikować elementy w miejscu.
  • for (const auto& x : v): x jest referencją tylko do odczytu. Używaj, gdy potrzebujesz tylko czytać.

Ten program modyfikuje kontener przez auto&:

Pułapka: napisz w tej pętli for (auto n : nums) (bez &), a n *= 10 po cichu zmieni tylko kopię, a nums zostanie nietknięte. Kompilator cię nie ostrzeże; pętla po prostu nie zrobi nic pożytecznego.

Co auto odrzuca

Zwykłe auto wnioskuje typ tak samo jak parametr funkcji przekazywany przez wartość: odrzuca const najwyższego poziomu, referencje i volatile. Oznacza to, że auto zawsze daje ci świeżą, modyfikowalną kopię, chyba że poprosisz o coś innego.

Jeśli chcesz zachować const albo uniknąć kopii, dodajesz kwalifikatory samodzielnie. Wzorzec polega na ozdobieniu auto dokładnie tak samo jak każdego innego typu:

Tak więc auto wnioskuje typ bazowy, a &, const i * to pokrętła, które dokładasz na wierzchu. auto to sam typ, a const auto& to referencja do niego tylko do odczytu.

Częste błędy i pułapki

auto oszczędza pisania, ale nie zwalnia z rozumienia typów. Kilka pułapek łapie początkujących:

Musisz zainicjalizować zmienną. auto nie ma z czego wnioskować przy pustej deklaracji, więc to twardy błąd kompilacji:

auto x;        // error: declaration of 'auto x' has no initializer
auto y = 0;    // w porządku

Literały całkowite to int, a nie double. auto half = 1 / 2; wnioskuje int i zapisuje 0, bo 1 / 2 to dzielenie całkowite wykonane zanim auto w ogóle zobaczy wynik. Typ podąża za wartością:

auto odrzuca referencję, więc uważaj na niespodziewane kopie. Jeśli funkcja zwraca referencję, a ty przechwycisz ją zwykłym auto, dostaniesz kopię, co w gorącej pętli bywa prawdziwym problemem wydajności (głęboka kopia dużego obiektu w każdej iteracji). Sięgaj po const auto&, gdy masz na myśli "popatrz, nie zabieraj".

Nie ukrywaj typu, gdy ma znaczenie. auto result = compute(); jest w porządku, gdy typ zwracany przez compute wynika jasno z kontekstu, ale jeśli czytelnik musi szukać, czym właściwie jest result, jawne wypisanie typu bywa bardziej przyjazne. auto służy do redukowania szumu, a nie do ukrywania intencji.

Dalej: stałe i const

Wiesz już, że auto celowo odrzuca const, chyba że poprosisz o jego zachowanie. To rodzi oczywiste pytanie: co właściwie gwarantuje const i kiedy w ogóle oznaczać wartość jako niezmienną? Następna strona omawia const, wyrażenia stałe i to, dlaczego zasada "domyślnie const" to jeden z najbardziej przydatnych nawyków w C++.

Najczęściej zadawane pytania

Co robi słowo kluczowe auto w C++?

auto każe kompilatorowi wywnioskować typ zmiennej z jej inicjalizatora. auto x = 5; sprawia, że x jest typu int; auto y = 3.14; sprawia, że y jest typu double. Typ jest ustalany w czasie kompilacji: auto to nie dynamiczne typowanie, tylko skrót, który oszczędza ci wypisywania typu.

Czy auto zachowuje const i referencje w C++?

Nie. Zwykłe auto odrzuca const najwyższego poziomu, referencje i volatile. Jeśli źródłem jest const int& r, to auto x = r; daje zwykłą kopię typu int. Aby je zachować, zapisujesz to jawnie: const auto& wiąże referencję tylko do odczytu bez kopiowania.

Czy można zadeklarować zmienną z auto bez inicjalizacji?

Nie. auto x; to błąd kompilacji, bo nie ma inicjalizatora, z którego kompilator mógłby wywnioskować typ. Każda zmienna auto musi dostać wartość w miejscu deklaracji.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ