Сколько стоит слишком много с ключевым словом С++ 11 auto?

Я использую новое ключевое слово auto, доступное в стандарте С++ 11, для сложных шаблонных типов, для чего я считаю, что он был разработан. Но я также использую его для таких вещей, как:

auto foo = std::make_shared<Foo>();

И более скептически для:

auto foo = bla(); // where bla() return a shared_ptr<Foo>

Я не видел много дискуссий по этой теме. Похоже, что auto можно было бы использовать, так как тип часто является формой документации и проверок здравомыслия. Где вы рисуете линию при использовании auto и каковы рекомендуемые варианты использования этой новой функции?

Прояснить: я не прошу философского мнения; Я прошу предполагаемого использования этого ключевого слова стандартным комитетом, возможно, с комментариями о том, как это предполагаемое использование реализуется на практике.

Боковое примечание: этот вопрос был перенесен на SE.Programmers, а затем обратно в Stack Overflow. Об этом можно узнать в этом мета-вопросе .

Ответ 1

Я думаю, что следует использовать ключевое слово 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)

Ответ 2

Используйте auto везде, где вы можете, особенно const auto, чтобы побочные эффекты были менее опасными. Вам не придется беспокоиться о типах, за исключением очевидных случаев, но они все равно будут статически проверены для вас, и вы можете избежать повторения. Если auto не представляется возможным, вы можете использовать decltype для семантического выражения типов в виде контрактов на основе выражений. Ваш код будет выглядеть по-другому, но это будет позитивное изменение.

Ответ 3

Легко. Используйте его, когда не заботится, что такое тип. Например

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++(), он вроде как утка, печатая в этом отношении.

Также тип lambdas не может быть записан, поэтому auto f = []... - хороший стиль. Альтернативой является листинг std::function, но это происходит с накладными расходами.

Я не могу представить себе "злоупотребление" auto. Самое близкое, что я могу себе представить, лишает вас явного преобразования в какой-то значительный тип, но для этого вы бы не использовали auto, вы бы построили объект желаемого типа.

Если вы можете удалить избыточность в своем коде без появления побочных эффектов, тогда должен быть хорошим.

Контрпримеры (заимствованные от других ответов):

auto i = SomeClass();
for (auto x = make_unsigned (y); ...)

Здесь мы заботимся о том, что такое тип, поэтому мы должны написать Someclass i; и for(unsigned x = y;...

Ответ 4

Пойдите для этого. Используйте auto в любом месте, где легче писать код.

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

Но если бы я работал над кодом с более чем несколькими строками типа

auto foo = bla();

где тип указывается в нулевое время, я могу изменить эти строки для включения типов. Первый пример велик, поскольку тип указан один раз, а auto избавляет нас от необходимости писать беспорядочные шаблонные типы дважды. Ура для С++++. Но явное указание времени нулевого типа, если оно не легко видно в соседней строке, заставляет меня нервничать, по крайней мере, на С++ и его непосредственных преемниках. Для других языков, предназначенных для работы на более высоком уровне с большей абстракцией, полиморфизмом и обличительностью, это прекрасно.

Ответ 5

Да, это может быть чрезмерно использовано в ущерб удобочитаемости. Я предлагаю использовать его в контекстах, где точные типы являются длинными или невыразимыми или не важны для читаемости, а переменные недолговечны. Например, тип итератора обычно длинный и не имеет значения, поэтому 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();

Ответ 6

В С++ и Beyond 2012 в панели Спросить нас все существует фантастический обмен между Андрей Александреску, Скотт Майерс и Херб Саттер говорят о том, когда использовать и не использовать auto. Пройдите в минуту 25:03 для обсуждения в течение 4 минут. Все три колонки дают отличные моменты, которые следует учитывать при использовании не auto.

Я очень призываю людей прийти к собственному выводу, но мой отвод был использовать auto везде, если:

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

Либеральное использование explicit помогает уменьшить интерес к последнему, что помогает свести к минимуму время, в течение которого первая проблема.

Перефразируя то, что сказал Херб, "если вы не делаете X, Y и Z, используйте auto. Узнайте, что X, Y и Z и продолжайте, и используйте auto всюду."

Ответ 7

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.

Ошибки, вызванные такими ошибками, являются основной причиной отладки. Одним из возможных способов устранения является то, что явным образом передаю результат ожидаемому типу, если вы пытаетесь использовать auto для стиля декларации слева направо.

auto C = Matrix(A * B); // The expression is now evaluated immediately.

Ответ 8

Я использую ограничение auto wihout и не сталкивался с какой-либо проблемой. Я даже иногда использую его для простых типов, таких как int. Это делает С++ языком более высокого уровня для меня и позволяет объявлять переменную в С++, как в python. После написания кода на Python я даже иногда пишу, например.

auto i = MyClass();

вместо

MyClass i;

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

Часто я не возражаю против точного типа объекта, меня больше интересует его обратная связь, и поскольку имена функций обычно говорят что-то о возвращаемых объектах, auto не болит: например, auto s = mycollection.size(), я могу догадаться, что s будет своего рода целым числом, и в редком случае, когда я забочусь о точном типе, пусть проверяет прототип функции (я имею в виду, я предпочитаю проверять, когда мне нужно info, а не априорно, когда код написан, на всякий случай это было бы полезно когда-нибудь, как в int_type s = mycollection.size()).

Относительно этого примера из принятого ответа:

for ( auto x = max_size; x > 0; --x )

В моем коде я все еще использую auto в этом случае, и если я хочу, чтобы x был неподписанным, я использую служебную функцию с именем say make_unsigned, которая четко выражает мои проблемы:

for ( auto x = make_unsigned(max_size); x > 0; --x )

Отказ от ответственности: я просто описываю свое использование, я не компетентен давать советы!

Ответ 9

Одна из основных проблем с программой на С++ позволяет использовать неинициализированную переменную. Это приводит нас к отвратительному не детерминированному поведению программы. Следует отметить, что современный компилятор теперь выдает соответствующие предупреждающие сообщения/сообщения, если шины программы используют его.

Чтобы проиллюстрировать это, рассмотрите ниже программу С++:

int main() {
    int x;
    int y = 0;
    y += x;
}

Если я скомпилирую эту программу с использованием современного компилятора (GCC), она выдает предупреждение. Такое предупреждение не может быть очень очевидно, если мы работаем с реальным сложным производственным кодом.

main.cpp: В функции 'int main()':

main.cpp: 4: 8: предупреждение: 'x' используется неинициализированным в этой функции [-Wuninitialized]

y + = x;

    ^

=============================================== ================================== Теперь, если мы изменим нашу программу, которая использует auto, тогда скомпилируем мы получим следующее:

int main() {
    auto x;
    auto y = 0;
    y += x;
}

main.cpp: В функции 'int main()':

main.cpp: 2: 10: ошибка: объявление 'auto x' не имеет инициализатора

 auto x;

      ^

С помощью auto невозможно использовать неинициализированную переменную. Это основное преимущество, которое мы можем получить (бесплатно), если мы начнем использовать auto.

Эта концепция и другая замечательная современная концепция С++ объясняются экспертом С++ Herb Shutter в его CppCon14 talk:

Вернуться к основам! Основы современного стиля С++

Ответ 10

Одна из опасностей, о которых я говорил, касается ссылок. например.

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();

Ответ 11

Используйте auto, где это имеет смысл для того, чтобы тип был выведен. Если у вас есть что-то, что вы знаете, это целое число, или вы знаете его как строку, просто используйте int/ std::string и т.д. Я бы не стал беспокоиться о "чрезмерной" языковой функции, если это не доходит до смехотворности или обфускации кода.

Это мое мнение в любом случае.

Ответ 12

auto ключевое слово может использоваться только для локальной переменной, а не для аргументов или членов класса/структуры. Таким образом, безопасно и выгодно использовать их в любом месте. Я использую их много. Тип выводится во время компиляции, отладчик показывает тип во время отладки, sizeof сообщает об этом правильно, decltype даст правильный тип - нет никакого вреда. Я не считаю auto чрезмерным, никогда!

Ответ 13

TL; DR: см. Правило большого пальца внизу.

Принятый ответ предполагает следующее правило:

Используйте auto когда трудно сказать, как писать тип с первого взгляда, но тип правой части выражения очевиден.

Но я бы сказал, что слишком ограничительный. Когда-то меня не волнуют типы, так как утверждение достаточно информативно, и я не хочу тратить время на то, чтобы понять тип. Что я имею в виду? Рассмотрим пример, который появился в некоторых ответах:

auto x = f();

Что делает это примером неправильного использования auto? Является ли это моим незнанием типа возвращения f()? Ну, это действительно поможет, если я это знаю, но... это не моя главная забота. Проблема в том, что x и f() довольно бессмысленны. Если бы у нас было:

auto nugget = mine_gold();

вместо этого, то мне обычно все равно, является ли возвращаемый тип функции очевидным или нет. Читая утверждение, я знаю, что делаю, и я достаточно знаю, что семантика возвращаемого значения не ощущает, что мне также нужно знать его тип.

Поэтому мой ответ: использовать auto всякий раз, когда компилятор разрешает его, если:

  • Вы чувствуете, что имя переменной вместе с выражением инициализации/присваивания не предоставляет достаточной информации о том, что делает оператор.
  • Вы чувствуете, что имя переменной вместе с выражением инициализации/присваивания предоставляет "вводящую в заблуждение" информацию о том, какой тип должен быть, т.е. Если вам нужно было угадать, что происходит вместо автоматического, вы могли бы сделать предположение - и это будет неправильно, и это ложное предположение имеет последствия позже в коде.
  • Вы хотите заставить другой тип (например, ссылку).

А также:

  • Предположим, что перед тем, как заменить auto конкретным типом, нужно указать значащее имя (которое не содержит название типа курса).

Ответ 14

Один из моих горьких переживаний с 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