С. Мейерс - Эффективный и современный C++ (1114942), страница 10
Текст из файла (страница 10)
1 - 1 .3.Сл едует запомнить•Выводимые типы часто можно просмотреть с помощью редакторов IDE, сообщений об ошибках компиляции и с использованием библиотеки Boost.Typelпdex.•Результаты, которые выдают некоторые инструменты, могут оказаться как неточными, так и бесполезными, так что понимание правил вывода типов в С++ является совершенно необходимым.1 .4. Как просмотреть выведенные типы47ГЛАВА 2О б ъ я в n ен и е autoКонцептуально объявление auto настолько простое, насколько может быть, но всеже сложнее, чем выглядит. Его применение экономит исходный текст, вводимый программистом, но при этом предупреждает появление вопросов корректности и производительности, над которыми вынужден мучиться программист при ручном объявлениитипов.
Кроме того, некоторые выводы типов aut o, хотя и послушно соблюдают предписанные алгоритмы, дают результаты, некорректные с точки зрения программиста. Когдатакое происходит, важно знать, как привести auto к верному ответу, поскольку возвратк указанию типов вручную - альтернатива, которой чаще всего лучше избегать.В этой короткой главе описаны основы работы с auto.2.1 . Предпочитайте auto явному объявnению типаЛегко и радостно написатьintх;Стоп! #@$! Я забыл инициализировать х, так что эта переменная имеет неопределенноезначение. Может быть. Но она может быть инициализирована и нулем - в зависимостиот контекста. Жуть!Ну, ладно.
Давайте лучше порадуемся объявлению локальной переменной, инициализированной разыменованием итератора:template<typename I t > / / Некий алгоритм, работающий сvoid dwim ( I t Ь, It е ) / / элементами из диапазона от Ь до еwhile (Ь ! = е )t:ypename s td : : i terator trai ts<I t> : : value_type_currVa lue=*Ь;Жуть. t ypename s td : : i t erator_t r a i t s < I t > : : value_t ype - просто чтобы записать типзначения, на которое указывает итератор? Нет, я такой радости не переживу. . .
#@$! Илия это уже говорил?"Ладно, третья попытка. Попробую объявить локальную переменную, тип которой такой же, как у лямбда-выражения. Но его тип известен только компилятору. #@$! (Этостановится привычкой . . . )Да что же это такое - никакого удовольствия от программирования на С++!Так не должно быть. И не будет! Мы дождались С++ l l, в котором все эти проблемырешены с помощью ключевого слова auto. Тип переменных, объявленных как aut o, выводится из их инициализатора, так что они обязаны быть инициализированными.
Этозначит - прощай проблема неинициализированных переменных:/ / Потенциально неинициализированная переменнаяxl ;auto х 2 ;// Ошибка ' Требуется инициализаторauto хЗ = О ; 1 1 Все отлично, переменная х корректно определенаintНет проблем и с объявлением локальной переменной, значением которой является разыменование итератора:template<typename It>void dwim ( It Ь , It е )/ / Все , как и ранееwhile ( Ь ! = е )auto currValue*Ь;А поскольку auto использует вывод типов (см. раздел 1 .2), он может представлять типы,известные только компиляторам:auto derefUPLess =//[ ] ( const std : : unique_ptr<Widget>& pl , / /const std : : uпique_ptr<Widget>& р2 ) //{ returп *pl < *р2 ; ) ;11Функция сравненияобъектов Widget , накоторые указываютstd : : unique ptr_Просто круто! В С++ 1 4 все еще круче, потому что параметры лямбда-выражений такжемогут включать auto:auto derefLess[ ] ( const auto& p l ,const auto& р2){ return *pl < *р2 ; ) ;11Функция сравнения в С++ 1 4 ,для значений, на которые1 1 указывает что угодно1 1 указателеобразное11Несмотря на всю крутость вы, вероятно, думаете, что можно обойтись и без auto для объявления переменной, которая хранит лямбда-выражение, поскольку мы можем использовать объект st d : : funct ion.
Это так, можем, но, возможно, это не то, что вы на самом деле подразумеваете. А может быть, вы сейчас думаете "А что это такое - объектstd : : funct i on? " Давайте разбираться.s t d : : funct i on - шаблон стандартной библиотеки С++ 1 1 , который обобщает идеюуказателя на функцию. В то время как указатели на функции могут указывать толькоSOГлава 2. Объявление a utoна функции, объект s t d : : funct i on может ссылаться на любой вызываемый объект,т.е.
на все, что может быть вызвано как функция. Так же как при создании указателяна функцию вы должны указать тип функции, на которую указываете (т.е. сигнатуруфункции, на которую хотите указать), вы должны указать тип функции, на которую будет ссылаться создаваемый объект std : : funct i on. Это делается с помощью параметрашаблона s t d : : funct ion. Например, для объявления объекта s t d : : funct ion с именемfunc, который может ссылаться на любой вызываемый объект, действующий так, какесли бы ero сигнатура былаbool ( const std: : unique_ptr<Widget> & , / / Сигнатура C++ l l дляconst std : : unique_ptr<Widget>& ) / / функции сравнения// std : : unique_ptr<Widget>следует написать следующее:std : : function<Ьool (const std: : unique_J1tr<Wiclqet>& ,const std: : unique_Jltr<Widqet>&) > func;Поскольку лямбда-выражения дают вызываемые объекты, замыкания могут храниться в объектах s t d : : funct ion.
Это означает, что можно объявить С + + I 1 -версиюdere fUPLess без применения auto следующим образом:std : : fl.Пlction<Ьool ( const std: : unique_J1tr<Wiclqet>& ,const std: : unique_J1tr<Wiclqet>&) >derefUPLess[ ] ( const std : : unique_pt r<Widget>& p l ,const std: : unique_ptr<Widget>& р2 ){ return *pl < *р2; } ;=Важно понимать, что, даже если оставить в стороне синтаксическую многословность и необходимость повторения типов параметров, использование std : : funct ion не то же самое,что использование auto.
Переменная, объявленная с использованием auto и хранящая замыкание, имеет тот же тип, что и замыкание, и как таковая использует только то количествопамяти, которое требуется замыканию. Тип переменной, объявленной как std : : funct i onи хранящей замыкание, представляет собой конкретизацию шаблона s t d : : f unct i on, которая имеет фиксированный размер для каждой заданной сигнатуры. Этот размер может быть не адекватным для замыкания, которое требуется хранить, и в этом случае конструктор s t d : : funct i on будет выделять для хранения замыкания динамическую память.В результате объект std : : funct i on использует больше памяти, чем объект, объявленныйс помощью auto.
Кроме того, из-за деталей реализации это ограничивает возможностивстраивания и приводит к косвенным вызовам функции, так что вызовы замыкания черезобъект std : : funct i on обычно выполняются медленнее, чем вызовы посредством объекта,объявленного как auto. Другими словами, подход с использованием std : : funct i on в общем случае более громоздкий, требующий больше памяти и более медленный, чем подходс помощью auto, и к тому же может приводить к генерации исключений, связанных с нехваткой памяти. Ну и, как вы уже видели в примерах выше, написать "auto" гораздо проще, чем указывать тип для инстанцирования std : : funct ion. В соревновании между auto--2.1 .
Предпочитайте a uto я вному обьявnению типа51и s t d : : funct i on для хранения замыкания побеждает auto. (Подобные аргументы можнопривести и в пользу предпочтения auto перед std : : funct ion для хранения результатов вызовов s td : : Ьi nd, но все равно в разделе 6.4 я делаю все, чтобы убедить вас использоватьвместо std : : Ьi nd лямбда-выражения" . )Преимущества auto выходят з а рамки избегания неинициализированных переменных, длинных объявлений переменных и возможности непосредственного хранения замыкания.
Кроме того, имеется возможность избежать того, что я называю проблемой"сокращений типа" (type shortcuts). Вот кое-что, что вы, вероятно, уже видели, а возможно, даже писали:std: : vector<int> v ;\ШSigned sz=v . s i ze ( ) ;Официальный возвращаемый тип v . s i ze ( ) - std : : vector<int> : : s i ze_type, но об этомзнает не так уж много разработчиков. s t d : : vect or<int > : : s i z e t ype определен как беззнаковый целочисленный тип, так что огромное количество программистов считают, чтоuns igned вполне достаточно, и пишут исходные тексты, подобные показанному выше.Это может иметь некоторые интересные последствия.
В 32-разрядной Windows, например,и uns i gned, и st d : : vector<int > : : s i ze_t ype имеют один и тот же размер, но в 64-разрядной Windows uns igned содержит 32 бита, а s t d : : vector<int > : : s i ze_type - 64 бита.Это означает, что код, который работал в 32-разрядной Windows, может вести себя некорректно в 64-разрядной Windows. И кому хочется тратить время на подобные вопросы припереносе приложения с 32-разрядной операционной системы на 64-разрядную?Применение auto гарантирует, что вам не придется этим заниматься:_auto sz=v . s i ze ( ) ; / / Тип s z - std : : vector<int> : : si ze_typeВсе еще не уверены в разумности применения auto? Тогда рассмотрите следующий код.s t d : : unordered_map<std : : string, int> m;for ( const std: : pa i r<std: : st ring, int>& р_ // Что-тоm)делаем с рВыглядит вполне разумно .