Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 32

Скотт Мейерс

Применение auto гарантирует, что вам не придется этим заниматься:

auto sz = v.size(); // Тип sz – std::vector<int>::size_type

Все еще не уверены в разумности применения auto? Тогда рассмотрите следующий код.

std::unordered_map<std::string, int> m;

for (const std::pair<std::string, int>& p: m) {

 … // Что-то делаем с p

}

Выглядит вполне разумно… но есть одна проблема. Вы ее не видите?

Чтобы разобраться, что здесь не так, надо вспомнить, что часть std::unordered_map, содержащая ключ, является константной, так что тип std::pair в хеш-таблице (которой является std::unordered_map) вовсе не std::pair<std::string, int>, а std::pair<const std::string, int>. Но переменная p в приведенном выше цикле объявлена иначе. В результате компилятор будет искать способ преобразовать объекты std::pair<const std::string, int> (хранящиеся в хеш-таблице) в объекты std::pair<std::string, int> (объявленный тип p). Этот способ — создание временного объекта типа, требуемого p, чтобы скопировать в него каждом объект из m с последующим связыванием ссылки p с этим временным объектом. В конце каждой итерации цикла временный объект уничтожается. Если этот цикл написан вами, вы, вероятно, будете удивлены его поведением, поскольку почти наверняка планировали просто связывать ссылку p с каждым элементом в m.

Такое непреднамеренное несоответствие легко лечится с помощью auto:

for (const auto& p : m) {

 … // Как и ранее

}

Это не просто более эффективно — это еще и менее многословно. Кроме того, этот код имеет очень привлекательную особенность — если вы возьмете адрес p, то можете быть уверены, что получите указатель на элемент в m. В коде, не использующем auto, вы получите указатель на временный объект — объект, который будет уничтожен в конце итерации цикла.

Два последних примера — запись unsigned там, где вы должны были написать std::vector<int>::size_type, и запись std::pair<std::string, int> там, где вы должны были написать std::pair<const std::string, int>, — демонстрируют, как явное указание типов может привести к неявному их преобразованию, которое вы не хотели и не ждали. Если вы используете в качестве типа целевой переменной auto, вам не надо беспокоиться о несоответствиях между типом объявленной переменной и типом инициализирующего ее выражения.

Таким образом, имеется несколько причин для предпочтительного применения auto по сравнению с явным объявлением типа. Но auto не является совершенным. Тип для каждой переменной, объявленной как auto, выводится из инициализирующего ее выражения, а некоторые инициализирующие выражения имеют типы, которые не предполагались и нежелательны. Условия, при которых возникают такие ситуации, и что при этом можно сделать, рассматриваются в разделах 1.2 и 2.2, поэтому здесь я не буду их рассматривать. Вместо этого я уделю внимание другому вопросу, который может вас волновать при использовании auto вместо традиционного объявления типа, — удобочитаемость полученного исходного текста.