Чи варто використовувати нову функцію "авто" C ++ 11, особливо в циклі?


20

Які плюси та мінуси використання autoключового слова, особливо для циклів?

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? Я пропустив це.
pdr

21
Ще краще, ви можете просто скористатися for (auto& it : x)(або без посилань, якщо хочете скопіювати)
Tamás Szelei

7
Мені здається, якщо ви пишете цикл, щоб перебрати вміст, xі ви навіть не знаєте, що xтаке, ви не повинні писати цю петлю в першу чергу ;-)
nikie

@fish: правила на основі циклів, що базуються на діапазоні, але я був би педантичним і робив: натомість "для (T & it: x)" при використанні діапазону на основі циклів, оскільки я вважаю, що використання авто є менш інформативним. Вид неправильного використання авто в моїй свідомості.
martiert

Боротьба з використанням 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

Це навіть не повний тип. Я пропустив пару аргументів шаблону.


8
+1 для прикладу, але це також щось говорить про стан "сучасного" C ++.
zvrba

22
@zvrba: Так - загальні засоби набагато потужніші, ніж у C #.
DeadMG

4
ось що таке typedef
gbjbaanb

19
@gbjbaanb Ні, це для чого auto. За дизайном. typedefдопомагає, але autoдопомагає більше.
Конрад Рудольф

1
typedef визнає недійсним аргумент про те, що "не використовуючи функцію автоматичного створення для дійсно довгих типів"
Michael

28

У вашому прикладі:

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

там має бути декларація для xвидимого. Тому тип itповинен бути очевидним. Якщо тип xне очевидний, то метод занадто довгий або клас занадто великий.


7
Крім того, xце дуже погана назва змінної для контейнера. У деяких ситуаціях можна, швидше за все, просто подивитися на (семантично цінне) ім’я та зробити висновок про можливі операції.
Макс

@Max: використовуюсь лише xяк загальний приклад, я схильний використовувати досить описові назви змінних.
Користувач

@User Звичайно, я не припускав, що це був приклад у реальному світі;)
Макс

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
Думаю, справа в тому, що з авто ви не знаєте, що ставите далі ... це ваш код конкретно "щось" чи це розпакування, пов'язане з типом даних, щоб дістатися до вашого об'єкта даних, який має метод "щось"
Майкл Шоу

1
@Ptolemy І я можу сказати: в двох інших кодах ви також не знаєте (як правило), що ставити далі: Tнастільки ж непрозорий для користувача auto. І все ж один повинен бути добре, а інший ні ?! Це не має сенсу. У випадку з ОП Tє резервним довільним типом. У реальному коді це може бути використання шаблонів (for typename std::vector<T>::iterator…)або інтерфейс класу. В обох випадках фактичний тип прихований від користувача, і все ж ми звичайно пишемо такий код без проблем.
Конрад Рудольф

1
Насправді так. Якщо його вектор, ви знаєте, що вам потрібно зробити -> і тоді у вас є доступ до вашого типу даних. Якщо його карта, ви знаєте, що вам потрібно зробити -> second-> і тоді у вас є доступ до вашого типу даних, якщо його автоматично, ви не знаєте, що вам потрібно зробити, щоб отримати доступ до свого типу даних. Здається, ви плутаєте "який тип даних, що міститься в колекції STL", і "який тип колекції STL у нас є". auto робить цю проблему ще гіршою.
Майкл Шоу

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 i = 0;

проти

int i = 0;

auto i = 0. Винні. Я це роблю. Але це тому, що я знаю, що 0це буквальний тип int. (і восьмерична константа ;-))
Лоран LA RIZZA

6

ТАК, ви повинні! autoне стирає тип; навіть якщо ви "не знаєте" що x.begin()таке, компілятор знає і повідомить про помилку, якщо ви спробуєте неправильно використовувати тип. Крім того, незвично емулювати mapз a vector<pair<Key,Value>>, тому використання коду autoбуде працювати для обох представлень словника.


4

Так, ви повинні використовувати autoяк правило за замовчуванням. Він має сильні переваги перед чітко вказаним типом:

  • Це не змушує вас вводити речі, про які компілятор уже знає.
  • Це змушує тип змінних "слідувати" за будь-якими змінами типів повернення.
  • Це запобігає мовчазному введенню неявних перетворень та розрізання в локальну ініціалізацію змінної.
  • Це усуває необхідність деяких явних обчислень типів у шаблонах.
  • Це усуває необхідність називати типи повернення з довгими іменами. (ті, які ви копіюєте та вставляєте з діагностики компілятора)

Тут ви маєте вибір. Також є випадки, коли у вас немає вибору:

  • Це дозволяє оголосити змінні типів, що не використовуються, наприклад тип лямбда.

За умови, що ви точно знаєте, що autoробить, це не має недоліків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.