Сколько это слишком много с ключевым словом auto C ++ 11? - PullRequest
205 голосов
/ 22 июня 2011

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

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

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

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

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

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

Примечание: этот вопрос был перенесен в SE.Programmersзатем вернитесь к переполнению стека.Обсуждение этого вопроса можно найти в этом мета-вопросе .

Ответы [ 14 ]

119 голосов
/ 22 июня 2011

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

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

47 голосов
/ 19 сентября 2011

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

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;...

44 голосов
/ 22 июня 2011

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

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

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

auto foo = bla();

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

36 голосов
/ 22 июня 2011

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

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

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

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

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

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

18 голосов
/ 20 февраля 2015

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.
10 голосов
/ 22 июня 2011

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

auto i = MyClass();

вместо

MyClass i;

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

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

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

3 голосов
/ 03 октября 2014

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

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

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 невозможно использовать неинициализированную переменную. Это главное преимущество, которое мы можем получить (бесплатно), если начнем использовать авто .

Эту концепцию и другую великую великую современную концепцию C ++ объясняет эксперт C ++ Herb Shutter в своем CppCon14 выступлении:

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

2 голосов
/ 21 февраля 2014

Одна опасность, которую я заметил, связана с рекомендациями.например,

MyBigObject& ref_to_big_object= big_object;
auto another_ref = ref_to_big_object; // ?

Проблема в том, что another_ref на самом деле не является ссылкой в ​​этом случае, это MyBigObject вместо MyBigObject &.Вы заканчиваете тем, что копируете большой объект, не осознавая этого.

Если вы получаете ссылку непосредственно из метода, вы можете не думать о том, что это на самом деле.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();
...