Сколько стоит слишком много с ключевым словом 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.Программисты, а затем вернуться к переполнению стека. Обсуждение об этом можно найти в этом мета вопрос.

649   14  

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 везде если:

  1. это вредит читабельности
  2. существует озабоченность по поводу автоматического преобразования типов (например, из конструкторов, присваивания, промежуточных типов шаблонов, неявного преобразования между целочисленными ширинами)

либеральное использование 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.

я использую auto wihout ограничение и не столкнулся с какой-либо проблемой. Я даже иногда в конечном итоге использовать его для простых типов, как 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--> чтобы быть без знака, то я использую служебную функцию с именем say make_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 говорят:

вернемся к основам! Основы современного стиля C++

использовать 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

    Ничего не найдено.