Сколько стоит слишком много с ключевым словом C++11 auto?
я использую новый auto ключевое слово, доступное в стандарте C++11 для сложных шаблонных типов, для которых, как я считаю, он был разработан. Но я также использую его для таких вещей, как:
auto foo = std::make_shared<Foo>();
и более скептически на:
auto foo = bla(); // where bla() return a shared_ptr<Foo>
Я не видел много дискуссий на эту тему. Кажется, что auto может быть перегружен, так как тип часто является формой документации и проверки. Где вы проводите линию с помощью auto а что такое рекомендуемые варианты использования этой новой функции?
чтобы уточнить: я не прошу философского мнения; я прошу о предполагаемом использовании этого ключевого слова стандартным комитетом, возможно, с комментариями о том, как это предполагаемое использование реализуется на практике.
Примечание стороны: этот вопрос был перемещен в SE.Программисты, а затем вернуться к переполнению стека. Обсуждение об этом можно найти в этом мета вопрос.
14 ответов:
Я думаю, что надо использовать
autoключевое слово всякий раз, когда трудно сказать, как пишут тип на первый взгляд, но тип в правой части выражения очевиден. Например, используя:my_multi_type::nth_index<2>::type::key_type::composite_key_type::\ key_extractor_tuple::tail_type::head_type::result_typeчтобы получить составной ключ типа
boost::multi_index, даже если вы знаете, что этоint. Вы не можете просто написатьintпотому что это может быть изменено в будущем. Я бы написалautoв этом случае.так что если
autoключевое слово улучшается читаемость в конкретном случае затем использовать его. Вы можете написатьautoкогда читателю очевидно, какой типautoпредставляет.вот несколько примеров:
auto foo = std::make_shared<Foo>(); // obvious auto foo = bla(); // unclear. don't know which type `foo` has const size_t max_size = 100; for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors // since max_size is unsigned std::vector<some_class> v; for ( auto it = v.begin(); it != v.end(); ++it ) // ok, since I know // that `it` has an iterator type (don't really care which one in this context)
использовать
autoвезде, где можно-особенноconst autoТак что побочные эффекты меньше беспокоят. Вам не придется беспокоиться о типах, за исключением очевидных случаев, но они все равно будут статически проверены для вас, и вы можете избежать некоторого повторения. Гдеautoне представляется возможным, вы можете использоватьdecltypeвыражать типы семантически как договоры на основе выражений. Ваш код будет выглядеть по-другому, но это будет позитивное изменение.
легко. Используйте его, когда вы не важно что это за тип. Например
for (const auto & i : some_container) { ...все, что меня здесь волнует, это
i- это все, что находится в контейнере.это немного похоже на typedefs.
typedef float Height; typedef double Weight; //.... Height h; Weight w;вот, мне все равно
hиwпоплавки или двойники, только что они любой тип подходит для выражения высоты и веса.или считать
for (auto i = some_container .begin (); ...здесь все, что меня волнует, это то, что это подходящий итератор, поддерживающий
operator++(), это вроде как утка набрав в этом отношении.также тип лямбд не может быть записан, так что
auto f = []...хороший стиль. Альтернативой является приведение кstd::functionно это идет с накладными расходами.я действительно не могу представить себе "злоупотребление"
auto. Самое близкое, что я могу себе представить, это лишение себя явного преобразования в какой-то значимый тип-но вы бы не использовалиautoдля этого ты создать объект нужного типа.если вы можете удалить избыточность в коде без побочных эффектов, то это должны будьте добры сделать это.
контрпримеры (заимствованные из чужих ответов):
auto i = SomeClass(); for (auto x = make_unsigned (y); ...)здесь нам все равно, какой тип, поэтому мы должны написать
Someclass i;иfor(unsigned x = y;...
пойти на это. Используйте
autoв любом месте это делает написание кода проще.каждая новая функция на любом языке будет чрезмерно использоваться по крайней мере некоторыми типами программистов. Только через умеренное злоупотребление некоторыми опытными программистами (не нубами) остальные опытные программисты узнают границы правильного использования. Чрезмерное использование обычно плохо, но может быть хорошо, потому что такое чрезмерное использование может привести к улучшению функции или лучшей функции для замены оно.
но если бы я работал над кодом с более чем несколькими строками, как
auto foo = bla();где тип указан ноль раз, я мог бы изменить эти строки, чтобы включить типы. Первый пример велик, так как тип указывается один раз, и
autoизбавляет нас от необходимости писать беспорядочные шаблонные типы дважды. Ура для C++++. Но явное отображение типа zero times, если он не легко виден в соседней строке, заставляет меня нервничать, по крайней мере, в C++ и его непосредственном преемники. Для других языков, предназначенных для работы на более высоком уровне с большей абстракцией, полиморфизмом и универсальностью, это нормально.
да, его можно злоупотреблять в ущерб удобочитаемости. Я предлагаю использовать его в контекстах, где точные типы длинны, или невыразимы, или не важны для читаемости, а переменные недолговечны. Например, тип итератора обычно длинен и не важен, поэтому
autoбудет работать:for(auto i = container.begin(); i != container.end(); ++i);
autoздесь не повредит читабельности.другой пример-тип правила парсера, который может быть длинным и запутанным. Сравните:
auto spaces = space & space & space;С
r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces = space & space & space;С другой стороны, когда тип известен и прост, гораздо лучше, если он явно указан:
int i = foo();, а не
auto i = foo();
At C++ и после 2012 года на Спросите Нас Что-Нибудь панель было!--11-->фантастический обмен между Андреем Александреску, Скотт Мейерс и Херб Саттер говорят о том, когда использовать и не использовать
auto. Перейти к минуте 25: 03 для 4-минутного обсуждения. Все три оратора дают отличные моменты, которые следует иметь в виду, когда не использоватьauto.Я очень призываю людей прийти к своему собственному выводу, но мой забрать было к использовать
autoвезде если:
- это вредит читабельности
- существует озабоченность по поводу автоматического преобразования типов (например, из конструкторов, присваивания, промежуточных типов шаблонов, неявного преобразования между целочисленными ширинами)
либеральное использование
explicitпомогает уменьшить беспокойство о последнем, что помогает свести к минимуму количество времени, когда первый является проблемой.перефразирование что Херб сказал: "Если вы не делаете X, Y и Z, используйте
auto. Узнайте, что такое X, Y и Z, и идите вперед и используйтеautoвезде."
autoможет быть очень опасно в сочетании с шаблонами выражений, которые часто используются библиотеками линейной алгебры, такими как Eigen или OpenCV.auto A = Matrix(...); auto B = Matrix(...); auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION. cout << C; // The expression is evaluated and gives the expected result. ... // <code modifying A or B> cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.ошибки, вызванные этим типом ошибок, являются серьезной болью для отладки. Одним из возможных решений является явное приведение результата к ожидаемому типу, если вы hellbent при использовании auto для стиля объявления слева направо.
auto C = Matrix(A * B); // The expression is now evaluated immediately.
я использую
autowihout ограничение и не столкнулся с какой-либо проблемой. Я даже иногда в конечном итоге использовать его для простых типов, какint. Это делает c++ языком более высокого уровня для меня и позволяет объявлять переменную в c++, как в python. После написания кода python, я даже иногда пишу напримерauto i = MyClass();вместо
MyClass i;это один случай, когда я бы сказал, что это злоупотребление
autoключевое слово.часто я не возражаю, что это точно тип объекта, меня больше интересует его fonctionality, и поскольку имена функций обычно говорят что-то об объектах, которые они возвращают,
autoне помешает: например,auto s = mycollection.size(), Я могу догадаться, чтоsбудет своего рода целым числом, и в редком случае, когда я забочусь о точном типе, давайте тогда проверим прототип функции (я имею в виду, что я предпочитаю проверять, когда мне нужна информация, а не априори, когда код написан, на всякий случай, когда это будет полезно когда-нибудь, как вint_type s = mycollection.size()).относительно этого примера из принятого ответа:
for ( auto x = max_size; x > 0; --x )в моем коде я все еще использую
autoв этом случае, и если я хочу!--12--> чтобы быть без знака, то я использую служебную функцию с именем saymake_unsigned, что ясно выражает мои опасения:for ( auto x = make_unsigned(max_size); x > 0; --x )отказ от ответственности: я просто описываю мой использовать, я не компетентен давать советы!
одна опасность, которую я отметил, это с точки зрения ссылок. например,
MyBigObject& ref_to_big_object= big_object; auto another_ref = ref_to_big_object; // ?проблема в том, что another_ref на самом деле не является ссылкой в этом случае это MyBigObject вместо MyBigObject&. Вы в конечном итоге копируете большой объект, не осознавая этого.
Если вы получаете ссылку непосредственно из метода вы можете не думать о том, что есть на самом деле.
auto another_ref = function_returning_ref_to_big_object();вам понадобится " auto& "или"const auto&"
MyBigObject& ref_to_big_object= big_object; auto& another_ref = ref_to_big_object; const auto& yet_another_ref = function_returning_ref_to_big_object();
одним из основные проблема с программой на C++ заключается в том, что она позволяет использовать неинициализированной переменной. Это приводит нас к неприятному недетерминированному поведению программы. Следует отметить, что современный компилятор теперь выдает соответствующие / предупреждающие сообщения, если программа устает его использовать.
просто чтобы проиллюстрировать это, рассмотрим ниже программу c++:
int main() { int x; int y = 0; y += x; }если я скомпилирую эту программу с помощью современного компилятора (GCC), он дает предупреждающий. Такого предупреждения может и не быть очень очевидно, если мы работаем с реальным сложным производственным кодом.
главная.cpp: в функции ' int main ()':
главная.cpp: 4: 8:предупреждение: 'x' используется неинициализированным в этой функции [-Wuninitialized]
y += x;
^================================================================================= Теперь если мы изменим нашу программу, которая использует авто, после компиляции получаем следующее:
int main() { auto x; auto y = 0; y += x; }главная.cpp: в функции ' int main ()':
главная.cpp: 2: 10:: объявление "auto x" не имеет инициализатора
auto x; ^С помощью auto невозможно использовать неинициализированную переменную. это главное преимущество, которое мы можем получить (бесплатно), если мы начнем использовать авто.
эта концепция и другие великие великие современные концепции C++ объясняется c++ expert Herb Shutter в своем CppCon14 говорят:
использовать
autoгде это имеет смысл для типа, который будет выведен. Если у вас есть что-то, что вы знаете, это целое число, или вы знаете, что это строка, просто используйте int / std::string и т. д. Я бы не стал беспокоиться о "чрезмерном использовании" языковой функции, если она не доходит до смешного или запутывает код.Это мое мнение в любом случае.
autoключевое слово может использоваться только для локальной переменной, а не для аргументов или членов класса/структуры. Таким образом, это безопасно и жизнеспособно, чтобы использовать их в любом месте вам нравится. Я использую их много. Тип выводится во время компиляции, отладчик показывает тип во время отладки,sizeofсообщает об этом правильно,decltypeдал бы правильный тип-нет никакого вреда. Я не считаюautoКак часто, никогда!
TL; DR: см. эмпирическое правило внизу.
The принято отвечать предлагает следующее эмпирическое правило:
использовать
autoвсякий раз, когда трудно сказать, как пишут тип на первый взгляд, но тип в правой части выражения очевиден.но я бы сказал, что это слишком ограничительный характер. Иногда я не забочусь о типах, так как заявление достаточно информативно без меня потрудившись взять время, чтобы выяснить тип. Что я имею в виду? Рассмотрим пример, который появился в некоторых ответах:
auto x = f();что делает это примером неправильного использования
auto? Это мое невежествоf()'ы возвращаемый тип? Ну, это действительно может помочь, если я знал это, но - это не моя главная забота. Что гораздо больше проблема в том, чтоxиf()довольно бессмысленно. Если бы мы имели:auto nugget = mine_gold();вместо этого, то я обычно не делаю заботьтесь о том, является ли возвращаемый тип функции очевидным или нет. Читая заявление, я знаю, что я делаю, и я знаю достаточно о том, что семантика возвращаемого значения не чувствует, что мне нужно также знать его тип.
поэтому мой ответ: используйте
autoвсякий раз, когда компилятор позволяет это, если:
- вы чувствуете, что имя переменной вместе с выражением инициализации / присвоения не предоставляют достаточной информации о том, что такое оператор делающий.
- вы чувствуете, что имя переменной вместе с выражением инициализации / назначения предоставляет "вводящую в заблуждение" информацию о том, каким должен быть тип - т. е. если бы вам пришлось угадать, что происходит вместо авто, вы могли бы сделать предположение - и это было бы неправильно, и это ложное предположение имеет последствия позже в коде.
- вы хотите заставить другой тип (например, ссылку).
и также:
- предпочтите дать осмысленное имя (которое, конечно, не содержит имя типа) перед заменой
autoс конкретным типом.
один из моих горьких опытов с
autoи использовать его с лямбда-выражениями:auto i = []() { return 0; }; cout<<"i = "<<i<<endl; // output: 1 !!!на самом деле, здесь
iразрешен к указателю функцииint(*)(). Это простоcout, но только представьте, какие плохие ошибки компиляции / выполнения он может вызвать при использовании сtemplate.вы должны избежать
autoС такими выражениями и поставить правильныйreturnтип (или контролируемыхdecltype())правильное использование для приведенного выше примера будет,
auto i = []() { return 0; }(); // and now i contains the result of calling the lambda
Comments