Спецификатор выведения типа: auto. C++11

  • Здесь частично описывается спецификатор выведения типа, который был введён в С++ в 2011г. Если Ваш компилятор более ранних лет, то auto работает в вашем компиляторе с очень большой вероятность совсем иным образом

В С++11 ключевое auto, которое существовало и до С++11, но очень редко использовалось, получило санкции на новый для себя вид деятельности: выводить тип. В 2011г. был принят стандарт С++11, где ключевое слово auto стало возможно использовать для автоматического вывода типа переменной.
Простейший способ применения ключевого слово auto выглядит так:

Можно легко выводить тип указателя на функцию, без грандиозной мозголомки:

В общем, тип для переменной задаётся такой же, как тип инициализатора. Инициализатор — это результат правой части.
Подобно обычному объявлению, где тип указывается один раз для некоторого множества однотипных переменных, можно указывать auto, с тем условием, что типы должны быть совместимы друг с другом:

Спецификатор auto гораздо более удобен в типах, которые или сложно определить самостоятельно, или долго писать:

Здесь, в коде над этой строкой, для переменной pv используется простой тип, но писать такое бывает утомительно, использование auto делает код более лаконичным и приятным для глаз.
Начиная с С++11 этот же код может выглядеть так:

К сожалению, выводимый тип спецификатором auto не всегда точно совпадает с типом инициализатора, иногда компилятор подправляет тип так, чтобы тип соответствовал обычным правилам инициализации. Вот, например, пример для ссылки:

Если думать, что auto всегда выведет нужный тип, то можно выстрелить себе в ногу. Этот пример достаточно простой, поэтому понять его можно относительно быстро. Можно ожидать, что с помощью auto выведется ссылочный тип, но выводится совсем не ссылочный тип, а обычный тип.

  • При использовании auto к ссылке, спецификатором auto выводится тип объекта, на который ссылка ссылается

Одна из запутанных тем — это указатели на константы и константные указатели. Абсолютно любой пионер той темы может заявить нечто наподобие эдакого: "Вроде и звучит просто, и рассказать могу, но как дело до кода доходит, вообще ничего не могу разобрать". Суть в том, что в указателе на константу можно менять сам указатель, но не значение, на которое он указывает, а в константном указателе можно менять значение, но не сам указатель. Так вот, к чему я это пишу, const разделяют на const верхнего уровня и на const нижнего уровня. Указатель — это самостоятельный объект, а то, на что он указывает, это его косвенная составляющая. Если квалификатор const применяется к самому указателю, то такой const называют const верхнего уровня, если квалификатор const применяется для косвенной составляющей, то такой const называют const нижнего уровня.

Возвращаемся к спецификатору типа, к auto . Спецификатор auto обычно откидывает const верхнего уровня.

Возможно, пример покажется сложным, но это не так. Понимание этого примера напрямую зависит от понимания природы указателя: есть объект — указатель, а есть объект — косвенная сущность указателя. Константу можно применить как самому указателю, так и к его косвенной сущности. Спецификатор auto отбрасывает такой const, который применяется к самому указателю. Вот и весь секрет примера.

Спецификатор auto допускает применение к себе ключевого слова const. Поэтому, чтобы заявить компилятору о своих претензиях на неизменяемость переменной, тип которой выводится с помощью auto, перед auto нужно поставить const квалификатор:

Подобный приём используется, если выводимый тип должен быть ссылочным типом:

В отличие от указателей ссылки не являются объектами, поэтому у них нет особого разделения на const верхнего или нижнего уровня. Все const для ссылок являются const нижнего уровня, они относятся к объекту, а не к ссылке, поэтому auto не игнорирует const у ссылок.

  • Тип переменных, объявленных как auto, выводится из инициализаторов, поэтому такие переменные обязаны быть инициализированы

В некоторых случаях использование auto может помочь избежать прячущейся проблемы. Трудно привести пример проявления, но описать одну из проблем такого вида можно.

Изучая документацию, можно видеть, что возвращается беззнаковое целое:
Что-нибудь эдакое: Member type size_type is an unsigned integral type. — в двух словах здесь говориться об unsigned int. Поэтому многие разработчики, опираясь на подобную информацию, считают, что код, написанный в показанном листинге вполне нормальный. Этот код нормальный до тех пор, пока программа работает в операционной системе, где разные типы совпадают свойствами вместимости. При переносе программы на другую систему возможно начало проблемы. В 32-х разрядных Windows тип v.size() и unsigned int эквивалентны, но на 64-х разрядных windows типы различаются: v.size() возвращает тип, который способен хранить 64 бита, а unsigned int способен хранить 32 бита. Двумя словами — рассогласования вместимости. Подробности я оставлю, они описаны в книге Мейерса. Суть дела в том, что использование auto помогает вывести правильный тип v.size(), благодаря чему подобного вида проблем можно избежать даже не зная о том, что на них возможно было наткнуться.

Вот ещё хорошие особенности auto:

  • auto умеет выводить типы, поэтому имеет представления о типах, известных только компилятору
  • В С++14 параметры лямбда выражений могут включать auto

Что такое лямбда-выражения в этой статье не оговаривается — это отдельная многословная тема.

  • auto отличается от std::function
  • подход с использованием std::function в общем случае более громоздкий, требующий больше памяти и более медленный, чем подход с помощью auto, и к тому же может приводить к генерации исключений, связанных с нехваткой памяти

Иногда программисту нужен точный тип, а auto выводит неточное представление, а приблизительное. В таких случаях не остаётся ничего, кроме того, чтобы использовать явно указываемый тип. Простейший пример такого проявления — вектор булевых значений:

По одному только типу, который получается в результате манипуляций, можно судить, что можно ждать подвоха, ведь логичнее будет, чтобы auto выводил bool, но он выводит странное vector::reference. Иногда эта особенность может приводить к неопределённому поведению. Я не могу объяснить, как именно всё происходит, что-то зависит от реализации типа vector<bool>::reference, что-то до меня не дошло. Этот кусочек материала взят из книги С. Мейерса. У него объясняется более подробно, но, мне иногда тяжеловато идут его пояснения.

Это, конечно, не всё, что можно рассказать о спецификаторе auto, но этого достаточно, чтобы познакомиться с ним и оценить некоторые преимущества. Главное его преимущество в том, что он очень удобен и сокращает код, главный недостаток в том, что он выводит типы не всегда абсолютно точно, а это потенциальный источник проблем.

Использованные материалы:

Язык программирования C++. Базовый курс, 5-е издание (Стенли Б. Липпман, Жози Лажойе, Барбара Э. Му)
Эффективный и современный С++. 42 рекомендации по использованию C++11 и C++14 (Скотт Мейерс)

4 комментария на «“Спецификатор выведения типа: auto. C++11”»

  1. Денис:

    #include <iostream>

    using namespace std;

    int main(){
        int i = 300;
        int *p = &i;
        const auto ptr = i;

        ptr++; //указатель, выведенный спецификатором auto приобрёл маркер неизминеяемости
    }

    Мне кажется, что у вас здесь опечатка. ptr не будет const указателем на int, а будет просто const int. 

    Надо было указать:  const auto ptr = p;

  2. Фина:

    Это не сработает, так как компилятор не может определить типы данных для параметров функции a и b во время компиляции.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/adam-trahtenberg/php-recepty-programmirovaniya-3-e-izdanie-9523718/?lfrom=15589587
Яндекс.Метрика