Я видел конференцию Херба Саттера, где он рекомендует каждому программисту C ++ использовать auto
.
Некоторое время назад мне приходилось читать код на C #, где var
он широко использовался, и этот код было очень трудно понять - каждый раз, var
когда я его использовал, мне приходилось проверять тип возврата с правой стороны. Иногда более одного раза, потому что я забыл тип переменной через некоторое время!
Я знаю, что компилятор знает тип, и мне не нужно его писать, но широко распространено мнение, что мы должны писать код для программистов, а не для компиляторов.
Я также знаю, что легче написать:
auto x = GetX();
чем:
someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();
Но это записывается только один раз, и GetX()
возвращаемый тип проверяется много раз, чтобы понять, какой тип x
имеет.
Это заставило меня задуматься - делает ли auto
код на C ++ труднее для понимания?
auto
может часто усложнять чтение, когда их уже трудно прочитать, т. е. функции слишком длинные, переменные имеют неправильные имена и т. д. В коротких функциях с переменными именами с приличными именами знание типов должно быть одним из простых или второстепенных # 2.auto
очень похоже на определение того, когда использоватьtypedef
. Вам решать, когда это мешает, а когда помогает.auto x = GetX();
, выбери лучшее имя, чем оно наx
самом деле говорит тебе, что оно делает в этом конкретном контексте ... это часто более полезно, чем его тип в любом случае.Ответы:
Краткий ответ: более полно, мое текущее мнение о
auto
том, что вы должны использоватьauto
по умолчанию, если вы явно не хотите конверсию. (Чуть точнее, «... если вы не хотите явно зафиксировать тип, что почти всегда происходит потому, что вы хотите преобразование».)Более длинный ответ и обоснование:
Пишите явный тип (а не
auto
) только тогда, когда вы действительно хотите явно зафиксировать тип, что почти всегда означает, что вы хотите явно получить преобразование в этот тип. Сверху головы я вспоминаю два основных случая:initializer_list
Сюрприз, которыйauto x = { 1 };
выводитinitializer_list
. Если вы не хотитеinitializer_list
, скажите тип - то есть явно попросите преобразование.auto x = matrix1 * matrix 2 + matrix3;
захватывает вспомогательный тип или тип прокси, не предназначенный для того, чтобы быть видимым для программиста. Во многих случаях это нормально и удобно для захвата этого типа, но иногда, если вы действительно хотите, чтобы он свернулся и выполнил вычисления, тогда произнесите тип - то есть снова явно запросите преобразование.В
auto
противном случае регулярно используйте по умолчанию, поскольку использованиеauto
позволяет избежать ошибок и делает ваш код более корректным, более понятным и надежным, а также более эффективным. Приблизительно по порядку от самого важного к наименее важному, в духе «напиши сначала для ясности и правильности»:auto
гарантии, вы получите правильный тип. Как говорится, если вы повторяете себя (говорите тип избыточно), вы можете и будете лгать (поймите неправильно). Вот обычный пример:void f( const vector<int>& v ) { for( /*…*
- в этот момент, если вы пишете тип итератора явно, вы хотите не забывать писатьconst_iterator
(не так ли?), Тогда какauto
просто получаете все правильно.auto
делает ваш код более устойчивым к изменениям, потому что при изменении типа выраженияauto
будет продолжать преобразовываться в правильный тип. Если вместо этого вы фиксируете явный тип, изменение типа выражения приведет к внедрению тихих преобразований, когда новый тип преобразуется в старый тип, или ненужных разрывов сборки, когда новый тип все еще работает, как старый тип, но не преобразуется в старый. тип (например, когда вы меняете amap
на aunordered_map
, что всегда хорошо, если вы не полагаетесь на порядок, используяauto
для своих итераторов, вы будете легко переключаться сmap<>::iterator
наunordered_map<>::iterator
, но используяmap<>::iterator
везде явно означает, что вы будете тратить свое драгоценное время на механические колебания кода, если только стажер не проходит мимо, и вы не можете отмахнуться от скучной работы над ними).auto
гарантирует, что неявное преобразование не произойдет, оно по умолчанию гарантирует лучшую производительность. Если вместо этого вы говорите тип, и он требует преобразования, вы часто молча получаете преобразование, независимо от того, ожидали вы этого или нет.auto
- ваш единственный хороший вариант для трудных для написания и невыразимых типов, таких как лямбда-выражения и помощники шаблонов, если не прибегать к повторяющимсяdecltype
выражениям или менее эффективным косвенным указаниям, таким какstd::function
.auto
меньше печатать. Я упоминаю это последнее для полноты, потому что это обычная причина, чтобы любить это, но это не самая большая причина использовать это.Следовательно: предпочитаю говорить
auto
по умолчанию. Он предлагает так много простоты, производительности и ясности, что вы наносите вред себе (и будущим сопровождающим вашего кода), если не делаете этого. Зафиксируйте явный тип только тогда, когда вы действительно это имеете в виду, что почти всегда означает, что вы хотите явного преобразования.Да, есть ( в настоящее время) а GotW об этом.
источник
auto x = static_cast<X>(y)
. Этоstatic_cast
дает понять, что преобразование сделано специально и позволяет избежать предупреждений компилятора о преобразовании. Обычно избегать предупреждений компилятора не так хорошо, но я согласен с тем, что не получаю предупреждения о преобразовании, которое я тщательно рассмотрел, когда писалstatic_cast
. Хотя я бы не стал этого делать, если сейчас нет предупреждений, но я хочу получать предупреждения в будущем, если типы изменяются потенциально опасным образом.auto
состоит в том, что мы должны стремиться программировать против интерфейсов (не в смысле ООП), а не против конкретных реализаций. Точно так же и с шаблонами. Вы жалуетесь на «трудно читаемый код», потому что у вас есть параметр типа шаблона,T
который используется везде? Нет, я так не думаю. В шаблонах мы также создаем код для интерфейса, так как многие люди называют это уткой во время компиляции.auto
.auto
переменной, но почти все они делают это правильно с явной спецификацией типа. Никто не использует IDE? Все используют только переменные int / float / bool? Все предпочитают внешнюю документацию для библиотек вместо самодокументированных заголовков?=
RHS не имеют особого смысла для любой другой интерпретации (штрих-скобочный список инициализации, но вам нужно знать, что вы инициализируете, что является оксюморономauto
). Тот , который является удивительным являетсяauto i{1}
также выводяinitializer_list
, несмотря подразумевающих не принимать это приготовилось Init-список , а принимать это выражение и использовать его тип ... но мы получаемinitializer_list
там. К счастью, C ++ 17 исправляет все это.Это индивидуальная ситуация.
Иногда это затрудняет понимание кода, иногда нет. Взять, к примеру:
определенно легко понять и определенно легче написать, чем фактическое объявление итератора.
Я уже некоторое время использую C ++, но я могу гарантировать, что при первом же запуске я получу ошибку компилятора, потому что я забуду об этом
const_iterator
и поначалу будуiterator
... :)Я бы использовал его для подобных случаев, но не там, где он фактически запутывает тип (например, в вашей ситуации), но это чисто субъективно.
источник
std::map<int, std::string>::const_iterator
, так что это не так, как если бы имя все равно говорило вам о типе.int
и значениеstd::string
. :)it->second
так как это постоянный итератор. Вся эта информация является повторением того, что в предыдущей строкеconst std::map<int, std::string>& x
. Многократное высказывание вещей иногда лучше информирует, но отнюдь не является общим правилом :-)for (anX : x)
сделать это еще более очевидным, мы просто повторяемx
. Обычный случай, когда вам нужен итератор, это когда вы модифицируете контейнер, ноx
этоconst&
Посмотрите на это по-другому. Ты пишешь:
или же:
Иногда это не помогает объяснить тип явно.
Решение о необходимости упоминания типа не совпадает с решением о том, хотите ли вы разделить код на несколько операторов путем определения промежуточных переменных. В C ++ 03 эти два были связаны, вы можете думать
auto
как способ разделить их.Иногда может быть полезно сделать явные типы:
против
В случаях, когда вы объявляете переменную, использование
auto
позволяет типу оставаться невысказанным, как во многих выражениях. Вы, вероятно, должны попытаться решить для себя, когда это помогает удобочитаемости и когда это мешает.Вы можете утверждать, что смешивание типов со знаком и без знака является ошибкой для начала (в действительности, некоторые утверждают, что вообще не следует использовать типы без знака). Причина это , возможно , ошибка в том , что она делает типы операндов жизненно важны из - за различное поведение. Если плохо знать типы ваших ценностей, то, вероятно, не так уж и плохо знать их. Таким образом, если код не сбивает с толку по другим причинам, это делает
auto
ОК, верно? ;-)В частности, при написании универсального кода существуют случаи, когда фактический тип переменной не должен быть важным, важно то, что он удовлетворяет требуемому интерфейсу. Так что
auto
обеспечивает уровень абстракции, где вы игнорируете тип (но, конечно, компилятор этого не знает). Работа на подходящем уровне абстракции может значительно улучшить читабельность, а работа на «неправильном» уровне делает чтение кода непростым делом.источник
auto
позволяет создавать именованные переменные с неименованными или неинтересными типами. Значимые имена могут быть полезны.sizeof
как unsigned.ИМО, вы смотрите на это в значительной степени наоборот.
Дело не в том, чтобы
auto
приводить к коду, который не читается или еще менее читается. Речь идет о (в надежде, что) наличии явного типа возвращаемого значения, что компенсирует тот факт, что (по-видимому) неясно, какой тип будет возвращен какой-либо конкретной функцией.По крайней мере, на мой взгляд, если у вас есть функция, тип возвращаемой которой не сразу очевиден, это ваша проблема. То, что делает функция, должно быть очевидно из ее названия, а тип возвращаемого значения должен быть очевиден из того, что она делает. Если нет, то это реальный источник проблемы.
Если здесь есть проблема, это не так
auto
. Это с остальной частью кода, и вполне вероятно, что явного типа достаточно, чтобы помочь вам увидеть и / или исправить основную проблему. Как только вы исправите эту реальную проблему, удобочитаемость кодаauto
, как правило, будет в порядке.Честно говоря, я должен добавить: я имел дело с несколькими случаями, когда такие вещи были не столь очевидны, как вам хотелось бы, и решение проблемы также было довольно несостоятельным. Просто для одного примера, я несколько лет назад консультировал компанию, которая ранее объединилась с другой компанией. В итоге они получили кодовую базу, которая была «сплочена вместе», а не объединена. Составляющие программы начали использовать разные (но довольно похожие) библиотеки для аналогичных целей, и хотя они работали над тем, чтобы объединить вещи более чисто, они все же сделали это. В большинстве случаев единственным способом угадать, какой тип будет возвращен данной функцией, было узнать, откуда возникла эта функция.
Даже в таком случае вы можете помочь прояснить несколько вещей. В этом случае весь код начинался в глобальном пространстве имен. Простое перемещение значительного количества в некоторые пространства имен устраняет конфликты имен и значительно облегчает отслеживание типов.
источник
Есть несколько причин, почему я не люблю auto для общего пользования:
Но подождите, это действительно хорошая идея? Что если тип имел значение в полдюжине этих вариантов использования, и теперь этот код ведет себя по-другому? Это также может неявно нарушать инкапсуляцию, изменяя не только входные значения, но и поведение самой частной реализации других классов, которые вызывают функцию.
1a. Я верю в понятие «самодокументируемый код». Причиной самодокументируемого кода является то, что комментарии имеют тенденцию устаревать, больше не отражая то, что делает код, в то время как сам код - если написан явным образом - не требует пояснений, всегда остается актуальным на его намерение, и не оставит вас в замешательстве с несвежими комментариями. Если типы могут быть изменены без необходимости изменять сам код, то сам код / переменные могут устареть. Например:
auto bThreadOK = CheckThreadHealth ();
За исключением проблемы, что CheckThreadHealth () в какой-то момент был подвергнут рефакторингу для возврата значения enum, указывающего состояние ошибки, если оно есть, вместо bool. Но человек, который сделал это изменение, пропустил проверку этой конкретной строки кода, и компилятор не помог, потому что он компилировался без предупреждений или ошибок.
Это даже вид работ, наверное. Я говорю, что это работает, потому что, несмотря на то, что вы делаете копию 500-байтовой структуры для каждой итерации цикла, так что вы можете проверить на ней одно значение, код все еще полностью функционален. Так что даже ваши модульные тесты не помогут вам понять, что плохой код скрывается за этим простым и невинно выглядящим автоматом. Большинство других людей, просматривающих файл, также не заметят его на первый взгляд.
Это также может быть ухудшено, если вы не знаете, что это за тип, но вы выбираете имя переменной, которое делает неверное предположение о том, что это такое, фактически достигая того же результата, что и в 1а, но с самого начала, а не после рефакторинга.
Мне кажется очевидным, что auto был введен в первую очередь как обходной путь для ужасного синтаксиса со стандартными типами шаблонов библиотеки. Вместо того, чтобы пытаться исправить синтаксис шаблона, с которым люди уже знакомы - что также может быть почти невозможно сделать из-за всего существующего кода, который он может сломать - добавьте ключевое слово, которое в основном скрывает проблему. По сути то, что вы могли бы назвать "взломать".
На самом деле у меня нет никаких разногласий с использованием auto со стандартными контейнерами библиотеки. Это, очевидно, то, для чего было создано ключевое слово, и функции в стандартной библиотеке вряд ли кардинально изменят назначение (или тип в этом отношении), что сделает auto относительно безопасным для использования. Но я бы очень осторожно использовал его с вашим собственным кодом и интерфейсами, которые могут быть гораздо более нестабильными и потенциально подвержены более фундаментальным изменениям.
Еще одно полезное приложение auto, расширяющее возможности языка, - создание временных файлов в макросах, не зависящих от типа. Это то, что вы не могли сделать раньше, но вы можете сделать это сейчас.
источник
auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);
. :-)Да, вам легче узнать тип вашей переменной, если вы ее не используете
auto
. Вопрос в следующем: нужно ли вам знать тип вашей переменной для чтения кода? Иногда ответ будет да, иногда нет. Например, когда вы получаете итератор от astd::vector<int>
, вам нужно знать, что этоstd::vector<int>::iterator
или будетauto iterator = ...;
достаточно? Все, что кто-либо хотел бы сделать с итератором, дается тем фактом, что это итератор - просто не имеет значения, какой конкретно тип.Используйте
auto
в тех ситуациях, когда это не делает ваш код сложнее для чтения.источник
Лично я использую
auto
только тогда, когда для программиста абсолютно очевидно, что это такое.Пример 1
Пример 2
источник
auto record = myObj.FindRecord(something)
было бы ясно, что типом переменной была запись. Или присвоение ему имениit
или тому подобного даст понять, что оно возвращает итератор. Обратите внимание, что даже если вы не использовалиauto
правильное присвоение имени переменной, это означало бы, что вам не нужно возвращаться к объявлению, чтобы посмотреть на тип из любой точки функции . Я удалил свое отрицательное голосование, потому что пример не является полным соломенным чучелом, но я все еще не покупаю аргумент здесь.MyClass::RecordTy record = myObj.FindRecord (something)
Этот вопрос требует мнения, которое варьируется от программиста к программисту, но я бы сказал, нет. На самом деле, во многих случаях, наоборот,
auto
может помочь сделать код проще для понимания, позволяя программисту сосредоточиться на логике, а не на мелочах.Это особенно верно в отношении сложных типов шаблонов. Вот упрощенный и надуманный пример. Что легче понять?
.. или же...
Кто-то скажет, что второе легче понять, кто-то скажет, что первое. Тем не менее, другие могут сказать, что беспричинное использование
auto
может привести к обескураживанию программистов, которые его используют, но это уже другая история.источник
std::map
приводят примеры, дополнительно со сложными аргументами шаблона.map
s. :)for
например, если итераторы недействительны в теле цикла и поэтому должны быть предварительно увеличены или не увеличены вообще.Пока много хороших ответов, но, чтобы сосредоточиться на исходном вопросе, я думаю, что Херб заходит слишком далеко в своем совете, чтобы использовать его
auto
свободно. Ваш пример - один из случаев, когда использованиеauto
явно ухудшает читабельность. Некоторые люди настаивают, что это не проблема современных IDE, где вы можете навести курсор на переменную и увидеть тип, но я не согласен: даже людям, которые всегда используют IDE, иногда приходится смотреть на фрагменты кода изолированно (подумайте об обзорах кода например) и IDE не поможет.Итог: используйте,
auto
когда это помогает: т.е. итераторы для циклов. Не используйте это, когда это заставляет читателя изо всех сил пытаться выяснить тип.источник
Я весьма удивлен, что никто еще не указал, что auto помогает, если нет четкого типа. В этом случае вы либо решаете эту проблему, используя #define или typedef в шаблоне, чтобы найти фактический используемый тип (а это иногда не тривиально), либо просто используете auto.
Предположим, у вас есть функция, которая возвращает что-то с платформо-зависимым типом:
Использование ведьмы вы бы предпочли?
или просто
Конечно, вы можете написать
а где-то хорошо, но делает
на самом деле рассказать что-нибудь еще о типе х? Он говорит, что это то, что возвращается оттуда, но это именно то, что авто. Это просто избыточно - «материал» здесь написан 3 раза - это, на мой взгляд, делает его менее читабельным, чем «автоматическая» версия.
источник
typedef
них.Читаемость субъективна; вам нужно посмотреть на ситуацию и решить, что лучше.
Как вы указали, без авто длинные объявления могут создавать много беспорядка. Но, как вы также указали, короткие объявления могут удалить информацию о типах, которая может быть полезной.
Кроме того, я бы добавил следующее: убедитесь, что вы смотрите на удобочитаемость, а не на удобство записи. Код, который легко написать, обычно нелегко читать, и наоборот. Например, если бы я писал, я бы предпочел авто. Если бы я читал, может быть, более длинные объявления.
Тогда есть последовательность; насколько это важно для вас? Желаете ли вы auto в одних частях и явные объявления в других, или один непротиворечивый метод?
источник
Я возьму в качестве преимущества менее читаемый код и буду поощрять программиста использовать его все больше и больше. Почему? Ясно, что если код, использующий auto, трудно читать, то и его будет сложно писать. Программист вынужден использовать значимое имя переменной , чтобы улучшить свою работу.
Возможно, в начале программист может не написать значимые имена переменных. Но в конечном итоге, исправляя ошибки или просматривая код, когда он / она вынужден объяснять код другим, или в не столь скором будущем он / она объясняет код специалистам по техническому обслуживанию, программист осознает ошибку и будет использовать значимое имя переменной в будущем.
источник
myComplexDerivedType
заставили людей писать имена переменных, например, чтобы компенсировать отсутствующий тип, который загромождает код повторением типа (везде, где используется переменная) и который побуждает людей опускать назначение переменной в ее имени. , Мой опыт показывает, что нет ничего более непродуктивного, чем активно создавать препятствия в коде.У меня есть два руководства:
Если тип переменной очевиден, утомительно писать или трудно определить, используйте auto.
Если вам нужно конкретное преобразование или тип результата неочевиден и может вызвать путаницу.
источник
Да. Это уменьшает многословие, но общее недоразумение состоит в том, что многословие уменьшает читабельность. Это верно только в том случае, если вы считаете удобочитаемость эстетической, а не фактической способностью интерпретировать код, который не увеличивается при использовании auto. В наиболее часто цитируемом примере векторных итераторов может показаться, что использование auto увеличивает читабельность вашего кода. С другой стороны, вы не всегда знаете, что даст вам ключевое слово auto. Вы должны следовать тому же логическому пути, что и компилятор, чтобы выполнить внутреннюю реконструкцию, и большую часть времени, особенно с итераторами, вы будете делать неправильные предположения.
В конце дня «авто» жертвует читабельностью кода и ясностью ради синтаксической и эстетической «чистоты» (которая необходима только потому, что итераторы имеют излишне запутанный синтаксис) и возможностью набирать на 10 символов меньше в любой строке. Это не стоит риска, или усилий, связанных с долгосрочными последствиями.
источник