ядро языка · вывод типовcore language · type deduction
Компилятор выводит тип переменной из инициализатора вместо того, чтобы ты писал его руками. Не «динамическая типизация» — тип фиксируется на этапе компиляции и не может измениться.The compiler deduces a variable's type from its initializer instead of you writing it out by hand. This is not "dynamic typing" — the type is fixed at compile time and can never change.
auto — ключевое слово, которое вместо явного типа заставляет компилятор вывести тип переменной из выражения-инициализатора по тем же правилам, что используются для вывода типов шаблонных параметров (template argument deduction).auto is a keyword that, instead of an explicit type, has the compiler deduce a variable's type from its initializer expression, following the same rules used for template argument deduction.
std::map<std::string, std::vector<int>>::const_iterator it =
m.find("key");
std::unique_ptr<Widget> w(new Widget());
auto it = m.find("key");
auto w = std::make_unique<Widget>();
Три независимые причины совпали в C++11:Three independent motivations converged in C++11:
Тип лямбды уникален и не имеет письменного обозначения — единственный способ сохранить лямбду в переменную (не через std::function, с overhead) — auto f = [](){...};.A lambda's type is unique and has no spellable name — the only way to store a lambda in a variable (without going through std::function, with its overhead) is auto f = [](){...};.
Итераторы контейнеров контейнеров, результаты bind, вложенные шаблоны — писать это руками не только долго, но и хрупко: тип меняется при рефакторинге сигнатуры, и его нужно менять во всех местах использования.Iterators of containers of containers, the result types of bind, nested templates — spelling these out by hand is not just tedious but fragile: the type changes whenever a signature is refactored, and every call site has to be updated in lockstep.
auto x = compute(); — тип объявлен один раз (в сигнатуре compute()), а не дважды (в сигнатуре и в объявлении переменной). Меньше шанс на несовпадение типа и на неявное дорогое преобразование при инициализации.auto x = compute(); declares the type once (in the signature of compute()), rather than twice (in the signature and again in the variable declaration). That leaves less room for a type mismatch or a silent, expensive conversion at initialization.
Вывод типа для auto использует те же правила, что и для параметра шаблона T при вызове template<class T> void f(T param) — с одной оговоркой про {} (см. грабли ниже). Это происходит целиком на этапе компиляции: auto не порождает никакого рантайм-кода и не создаёт тип «переменного размера» — после компиляции переменная имеет ровно такой же машинный код, как если бы тип был написан явно.Type deduction for auto follows the same rules as for the template parameter T in a call to template<class T> void f(T param) — with one exception around {} (see the gotchas below). This all happens entirely at compile time: auto generates no runtime code and doesn't create some "variable-size" type — after compilation, the variable has exactly the same machine code as if the type had been written out explicitly.
Ключевое следствие правил вывода: auto по умолчанию отбрасывает ссылки и верхнеуровневый const/volatile у инициализатора, если явно не написать auto& / const auto& / auto&&:A key consequence of the deduction rules: auto strips references and top-level const/volatile from the initializer by default, unless you explicitly write auto& / const auto& / auto&&:
const int& cr = get_ref();
auto a = cr; // int — const и & отброшены, будет копия// int — const and & are dropped, this is a copy
auto& b = cr; // const int& — const сохранён, потому что явная ссылка// const int& — const is preserved because the reference is explicit
auto&& c = cr; // const int& — universal reference, см. rvalue-references.html// const int& — a universal reference, see rvalue-references.html
Единственное расхождение с template deduction: auto с фигурными скобками выводит std::initializer_list<int>, а не int[3] и не единственный int. auto x = {1}; — тоже initializer_list<int> с одним элементом, не int — частый источник удивления.The one place auto diverges from template deduction: with brace-init, auto deduces std::initializer_list<int>, not int[3] and not a lone int. auto x = {1}; is also an initializer_list<int> with one element, not an int — a frequent source of surprise.
auto row = table[i]; может незаметно скопировать тяжёлый объект, если operator[] возвращает по значению, а не по ссылке — тип не виден в объявлении, поэтому легко не заметить копирование. Смотреть сигнатуру возвращаемого типа, когда важна производительность.auto row = table[i]; can silently copy a heavy object if operator[] returns by value rather than by reference — the type isn't spelled out in the declaration, so the copy is easy to miss. Check the return type's signature whenever performance matters.
В отличие от явного типа, auto требует смотреть на инициализатор, чтобы понять, что за тип у переменной — код с длинной цепочкой вызовов до auto x = a.b().c().d(); может быть менее самодокументируемым. Компенсируется хорошими именами переменных и IDE, которая покажет выведенный тип.Unlike an explicit type, auto requires looking at the initializer to figure out the variable's type — code with a long call chain like auto x = a.b().c().d(); can end up less self-documenting. This is compensated for by good variable names and an IDE that shows the deduced type on hover.