Должен ли я использовать новую функцию auto в C ++ 11, особенно в циклах?

20

Каковы плюсы / минусы использования autoключевого слова, особенно в циклах for?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Похоже, если вы не знаете, есть ли у вас итератор для карты или вектора, вы не знаете, использовать firstли secondили просто напрямую обращаться к свойствам объекта, нет?

Это напоминает мне спор о том, стоит ли использовать ключевое слово в C # var. У меня сложилось впечатление, что в мире C ++ люди готовы принять autoключевое слово с меньшими усилиями, чем varв мире C #. Для меня мой первый инстинкт заключается в том, что мне нравится знать тип переменной, чтобы я мог знать, какие операции я могу ожидать над ней.

пользователь
источник
3
Подождите! Была драка о том, использовать ли var? Я пропустил это.
фунтовые
21
Более того, вы можете просто использовать for (auto& it : x)(или без ссылки, если вы хотите копировать)
Tamás Szelei
7
Мне кажется, если вы пишете цикл для перебора содержимого, xи вы даже не знаете, что это xтакое, вы не должны писать этот цикл в первую очередь ;-)
nikie
@fish: правила для циклов на основе диапазона, но я бы был педантичен и сделал бы: 'for (T & it: x)' вместо этого при использовании циклов на основе диапазона, так как я чувствую, что использование auto там менее информативно. Вид неправильного использования авто в моей голове.
Мартиерт
Борьба за использование var была немного глупой, особенно в ретроспективе. См. Грузовое культовое программирование .
Крейг

Ответы:

38

Мотивации в C ++ более экстремальны, поскольку типы могут стать значительно более запутанными и сложными, чем типы C #, из-за метапрограммирования и других вещей. autoбыстрее писать и читать и более гибкий / обслуживаемый, чем явный тип. Я имею в виду, ты хочешь начать печатать

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

Это даже не полный тип. Я пропустил пару аргументов шаблона.

DeadMG
источник
8
+1 для примера, но это также говорит о состоянии "современного" C ++.
zvrba
22
@zvrba: Да, общие возможности намного мощнее, чем в C #.
DeadMG
4
вот для чего typedef
gbjbaanb
19
@gbjbaanb Нет, вот для чего auto. По дизайну. typedefпомогает, но autoпомогает больше.
Конрад Рудольф
1
typedef лишает законной силы аргумент, что «не использовать auto делает для действительно длинных типов»
Michael
28

В вашем примере:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

должна быть декларация для xвидимого. Поэтому тип itдолжен быть очевидным. Если тип xнеочевиден, то метод слишком длинный или класс слишком большой.

Кевин Клайн
источник
7
Кроме того, xэто очень плохое имя переменной для контейнера. В некоторых ситуациях вы, скорее всего, можете просто посмотреть на (семантически ценное) имя и сделать вывод о возможных операциях.
Макс
@Max: используется только xв качестве общего примера, я склонен использовать достаточно описательные имена переменных.
Пользователь
@ Пользователь Конечно, я не предполагал, что это был реальный пример;)
Макс
14

Возражение ! Загруженный вопрос.

Можете ли вы объяснить мне, почему третий код содержит ??, а первый и второй нет? Справедливости ради, ваш код должен выглядеть следующим образом:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Там. Та же проблема, даже если вы не использовали auto.

И во всех случаях ответ один и тот же: контекст имеет значение . Вы не можете осмысленно говорить о куске кода изолированно. Даже если бы вы не использовали шаблоны, а использовали какой-то конкретный тип, это только переместило бы проблему куда-нибудь еще, поскольку читатель вашего кода должен был бы знать об объявлении указанного типа.

Если использование autoв таких ситуациях делает ваш код нечитаемым, вы должны воспринимать это как предупреждение о том, что с вашим дизайном кода что-то не так. Конечно, есть случаи, когда низкоуровневые детали имеют значение (например, при работе с битовыми операциями или устаревшим API), когда явный тип может способствовать удобочитаемости. Но в целом - нет.

Что касается var(поскольку вы прямо упомянули об этом), в сообществе C # также существует широкий консенсус в отношении использования var. Аргументы против его использования обычно основаны на заблуждениях .

Конрад Рудольф
источник
1
Я думаю, что суть в том, что с auto вы не знаете, что вы помещаете дальше ... это ваш код "что-то" или это распаковка, связанная с типом данных, чтобы добраться до вашего объекта данных, который имеет метод "что-то"
Майкл Шоу
1
@Ptolemy И моя точка зрения такова: в двух других кодах вы также не знаете (как правило), что ставить дальше: Tэто настолько же непрозрачно для пользователя, как и auto. И все же один должен быть в порядке, а другой нет ?! Это не имеет смысла. В случае OP, Tявляется заменой для произвольного типа. В реальном коде это может быть использование шаблонов (for typename std::vector<T>::iterator…)или интерфейс класса. В обоих случаях фактический тип скрыт от пользователя, и все же мы обычно пишем такой код без проблем.
Конрад Рудольф
1
На самом деле, да, вы делаете. Если это вектор, вы знаете, что вам нужно сделать -> и тогда у вас есть доступ к вашему типу данных. Если это карта, вы знаете, что вам нужно сделать -> second->, и тогда у вас есть доступ к вашему типу данных, если он автоматический, вы не знаете, что вам нужно сделать, чтобы получить доступ к вашему типу данных. Похоже, вы путаете «что тип данных содержится в коллекции STL» с «какой тип коллекции STL у нас есть». Авто усугубляет эту проблему.
Майкл Шоу
1
@Ptolemy Все эти аргументы так же верны при использовании auto. Понятно, какие операции xподдерживает из контекста. Фактически, тип не дает вам никакой дополнительной информации: в любом случае вам понадобится какой-то дополнительный (IDE, документация, знания / память), чтобы сообщить вам набор поддерживаемых операций.
Конрад Рудольф
1
@Ptolemy Это только справедливо , если вы находитесь в весьма свернутой ситуации , что вы не знаете , что beginвозвращается , но вы же знаете , что std::vector<>::iteratorесть. И вам нужно использовать плохой инструмент программирования, который не может дать вам эту информацию тривиально. Это очень запутанный. В действительности вы либо знаете и то, beginи iteratorдругое, или нет, и вам следует использовать IDE или редактор, который может легко предоставить вам соответствующую информацию. Это может сделать каждый современный IDE и редактор программ.
Конрад Рудольф
11

PRO

Ваш код :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

не собирается компилироваться из-за зависимого от шаблона имени.

Это правильный синтаксис:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Посмотрите, как долго объявление типа. Это говорит о том, почему autoбыло введено ключевое слово. Эта :

for( auto it = x.begin(); it != x.end(); i++)

является более кратким. Итак, это профессионал.


ПРОТИВ

Вы должны быть немного осторожны. С ключевым словом autoвы получите тип, который вы объявили.

Например :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

против

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

В заключение: да, вы должны это, но не злоупотреблять этим. Некоторые люди склонны использовать его слишком часто, и везде ставят auto, как в следующем примере:

auto i = 0;

против

int i = 0;
BЈовић
источник
auto i = 0, Guilty. Я делаю это. Но это потому, что я знаю, что 0это буквальный тип int. (и восьмеричная константа ;-))
Laurent LA RIZZA
6

Да, ты должен! autoне стирает тип; даже если вы «не знаете», что x.begin()такое, компилятор знает и сообщит об ошибке, если вы попытаетесь использовать тип неправильно. Кроме того, нет ничего необычного в том, чтобы эмулировать mapс помощью a vector<pair<Key,Value>>, поэтому использование кода autoбудет работать для обоих представлений в словаре.

zvrba
источник
4

Да, вы должны использовать autoв качестве правила по умолчанию. Он имеет ряд преимуществ по сравнению с явным указанием типа:

  • Это не заставляет вас печатать то, о чем уже знает компилятор.
  • Это заставляет тип переменных «следовать за» любым изменением типов возвращаемых значений.
  • Это предотвращает тихое введение неявных преобразований и секционирование при инициализации локальной переменной.
  • Это устраняет необходимость некоторых явных вычислений типов в шаблонах.
  • Это устраняет необходимость именования возвращаемых типов с длинными именами. (те, которые вы копируете из диагностики компилятора)

Здесь у вас есть выбор. Есть также случаи, когда у вас нет выбора:

  • Это позволяет объявлять переменные невыбираемых типов, таких как лямбда-типы.

При условии, что вы точно знаете, что делаете auto, у него нет недостатков.

Laurent LA RIZZA
источник