Пусть тип выбирает компилятор
На предыдущей странице вы видели, что у каждой переменной C++ есть фиксированный тип — int, double, std::string и так далее. Писать этот тип вручную нормально для int count = 0;, но это становится загромождающим, как только типы удлиняются. Ключевое слово auto (C++11 и более поздние) позволяет компилятору вывести тип из значения, которое вы присваиваете, так что вы пишете значение один раз и предоставляете компилятору заполнить остальное.
Ключевая мысль: auto это не динамическая типизация. У каждой переменной по-прежнему есть один конкретный тип, зафиксированный во время компиляции. auto просто избавляет вас от его написания.
Почему auto оправдывает себя
Для коротких, очевидных типов auto count = 0; и int count = 0; одинаково читаемы. По-настоящему auto окупается с длинными, повторяющимися именами типов — теми, что получаются из контейнеров и итераторов стандартной библиотеки.
Сравните многословную версию с версией на auto:
// Без auto - по сути, тип написан дважды
std::vector<std::pair<std::string, int>>::iterator it = scores.begin();
// С auto - компилятор уже знает тип
auto it = scores.begin();
Обе объявляют ровно один и тот же тип итератора. Вторую легче читать, и она не рассинхронизируется, если позже вы смените scores на другой контейнер.
Вот это в полной программе:
auto в циклах for на основе диапазона
Самое распространённое место, где вы встретите auto, это цикл for на основе диапазона. Вы почти никогда не захотите писать тип элемента вручную, и то, как вы пишете auto, решает, получите ли вы копию или ссылку.
Три варианта, которые вы увидите, и что означает каждый:
for (auto x : v)-xэто копия каждого элемента. Дёшево дляint, расточительно для крупных объектов.for (auto& x : v)-xэто ссылка; вы можете изменять элементы на месте.for (const auto& x : v)-xэто ссылка только для чтения. Используйте её, когда вам нужно только читать.
Следующая программа изменяет контейнер через auto&:
Ловушка: напишите for (auto n : nums) (без &) в этом цикле, и n *= 10 молча изменит только копию, оставив nums нетронутым. Компилятор не предупредит вас — цикл просто не делает ничего полезного.
Что отбрасывает auto
Простой auto выводит тип так же, как параметр функции, передаваемый по значению: он отбрасывает const верхнего уровня, ссылки и volatile. Это означает, что auto всегда даёт вам свежую, изменяемую копию, если вы не попросите об ином.
Если вы хотите сохранить const или избежать копии, вы добавляете квалификаторы сами. Приём в том, чтобы украшать auto так же, как вы украшали бы любой тип:
Итак, auto выводит базовый тип; &, const и * это ручки, которые вы добавляете сверху. auto это тип, а const auto& это ссылка на него только для чтения.
Распространённые ошибки и ловушки
auto устраняет усилия по набору, а не необходимость понимать типы. Несколько ловушек подстерегают новичков:
Вы обязаны инициализировать. auto нечего выводить из пустого объявления, поэтому это однозначная ошибка компиляции:
auto x; // error: declaration of 'auto x' has no initializer
auto y = 0; // нормально
Целочисленные литералы это int, а не double. auto half = 1 / 2; выводит int и хранит 0, потому что 1 / 2 это целочисленное деление до того, как auto его вообще увидит. Тип следует за значением:
auto отбрасывает ссылку — остерегайтесь сюрпризов с висячими копиями. Если функция возвращает ссылку, а вы захватываете её простым auto, вы получаете копию, что иногда является настоящей ошибкой производительности в горячем цикле (глубокая копия большого объекта на каждой итерации). Прибегайте к const auto&, когда имеете в виду «смотреть, но не брать».
Не скрывайте тип, когда он важен. auto result = compute(); нормально, когда тип возвращаемого значения compute очевиден из контекста, но если читателю приходится выискивать, чем на самом деле является result, выписать тип может быть более дружелюбным выбором. auto нужен для уменьшения шума, а не для сокрытия намерения.
Далее: Константы и const
Теперь вы видели, что auto намеренно отбрасывает const, если вы не попросите его сохранить, — что поднимает очевидный вопрос: что на самом деле гарантирует const и когда вообще стоит помечать значение как неизменяемое? Следующая страница углубляется в const, константные выражения и в то, почему «делай его const по умолчанию» это одна из самых полезных привычек в C++.
Часто задаваемые вопросы
Что делает ключевое слово auto в C++?
auto указывает компилятору вывести тип переменной из её инициализатора. auto x = 5; делает x типом int; auto y = 3.14; делает y типом double. Тип фиксируется во время компиляции — auto это не динамическая типизация, а способ не писать тип самому.
Сохраняет ли auto const и ссылки в C++?
Нет. Простой auto отбрасывает const верхнего уровня, ссылки и volatile. Если источник const int& r, то auto x = r; даёт обычную копию int. Чтобы их сохранить, вы указываете это явно: используйте const auto&, чтобы привязать ссылку только для чтения без копирования.
Можно ли объявить переменную с auto без инициализации?
Нет. auto x; это ошибка компиляции, потому что нет инициализатора, из которого компилятор мог бы вывести тип. Каждой переменной auto нужно задать значение в точке её объявления.