Читать «Эффективный и современный С++. 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
вместо традиционного объявления типа, — удобочитаемость полученного исходного текста.