Есть ли недостаток в объявлении переменных с auto в С++?

Похоже, что auto была довольно значительной функцией, которая должна быть добавлена ​​в С++ 11, которая, как представляется, следует за множеством новых языков. Как и в случае с языком, подобным Python, я не видел явного объявления переменной (я не уверен, что это возможно с использованием стандартов Python).

Есть ли недостаток в использовании auto для объявления переменных вместо их явного объявления?

Ответ 1

Вы только спрашивали о недостатках, поэтому я выделяю некоторые из них. При использовании хорошо, auto имеет ряд преимуществ. Недостатки проистекают из-за простоты злоупотреблений и из-за повышенной вероятности того, что код будет вести себя непреднамеренно.

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

Учитывая объявление типа

auto result = CallSomeFunction(x,y,z);

вы не обязательно знаете, какой тип result. Это может быть int. Это может быть указатель. Это может быть что-то другое. Все они поддерживают разные операции. Вы также можете значительно изменить код с помощью незначительных изменений, например

auto result = CallSomeFunction(a,y,z);

потому что, в зависимости от того, какие перегрузки существуют для CallSomeFunction(), тип результата может быть совершенно другим, и поэтому следующий код может вести себя совершенно иначе, чем предполагалось. Вы можете внезапно вызвать сообщения об ошибках в более позднем коде (например, затем попытаться разыменовать int, пытаясь изменить что-то, что сейчас const). Более зловещее изменение - это то, где ваше изменение проплывает мимо компилятора, но последующий код ведет себя по-разному и неизвестно - возможно, глючит.

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

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

Ответ 2

Это не является недостатком auto принципиально, но на практике это кажется проблемой для некоторых. В принципе, некоторые люди: а) лечат auto как спаситель для типов и закрывают свой мозг при его использовании, или б) забывают, что auto всегда выводит типы значений. Это заставляет людей делать такие вещи:

auto x = my_obj.method_that_returns_reference();

К сожалению, мы просто скопировали некоторый объект. Это часто либо ошибка, либо производительность не работают. Затем вы можете качаться и наоборот:

const auto& stuff = *func_that_returns_unique_ptr();

Теперь вы получаете свисающую ссылку. Эти проблемы не вызваны auto вообще, поэтому я не считаю их законными аргументами против него. Но похоже, что auto делает эту проблему более распространенной (по моему личному опыту) по причинам, перечисленным в начале.

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

Ответ 3

В других ответах упоминаются недостатки типа "вы действительно не знаете, что такое тип переменной". Я бы сказал, что это во многом связано с неряшливым соглашением именования в коде. Если ваши интерфейсы четко обозначены, вам не нужно заботиться о том, какой именно тип. Конечно, auto result = callSomeFunction(a, b); не говорит вам много. Но auto valid = isValid(xmlFile, schema); говорит вам достаточно использовать valid, не заботясь о том, каков его точный тип. В конце концов, всего лишь if (callSomeFunction(a, b)), вы тоже не знаете тип. То же самое с любыми другими временными объектами подвыражения. Поэтому я не считаю это настоящим недостатком auto.

Я бы сказал, что его основным недостатком является то, что иногда точный тип возврата не является тем, с которым вы хотите работать. В действительности иногда фактический тип возврата отличается от типа логического возврата как деталь реализации/оптимизации. Примеры шаблонов являются ярким примером. Скажем, мы имеем это:

SomeType operator* (const Matrix &lhs, const Vector &rhs);

Логически мы ожидаем, что SomeType будет Vector, и мы определенно хотим рассматривать его как таковое в нашем коде. Однако, возможно, что для целей оптимизации библиотека алгебр, которую мы используем, реализует шаблоны выражений, а фактический тип возвращаемого значения таков:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Теперь проблема заключается в том, что MultExpression<Matrix, Vector> будет, во всяком случае, хранить a const Matrix& и const Vector& внутренне; он ожидает, что он преобразуется в Vector до конца своего полного выражения. Если у нас есть этот код, все хорошо:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

Однако, если бы мы использовали auto здесь, у нас могли бы возникнуть проблемы:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}

Ответ 4

Один из недостатков заключается в том, что иногда вы не можете объявить const_iterator с помощью auto. Вы получите обычный (не const) итератор в этом примере кода, взятого из этого вопроса:

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");

Ответ 5

Это делает ваш код немного сложнее или утомительно, чтобы читать. Представьте себе что-то вроде этого:

auto output = doSomethingWithData(variables);

Теперь, чтобы выяснить тип вывода, вам придется отслеживать подпись функции doSomethingWithData.

Ответ 6

Как этот разработчик, я ненавижу auto. Вернее, я ненавижу, как люди злоупотребляют auto.

Я из (сильного) мнения, что auto предназначен для того, чтобы помочь вам написать общий код, не для сокращения ввода.
С++ - это язык, целью которого является дать вам возможность писать надежный код не, чтобы свести к минимуму время разработки.
Это довольно очевидно из многих функций С++, но, к сожалению, некоторые из более новых, таких как auto, позволяют сократить ввод в заблуждение людей, думая, что они должны начать лениться при наборе текста.

В течение auto дней люди использовали typedef s, что было здорово, потому что typedef разрешал разработчику библиотеки помочь вам разобраться, какой тип возврата должен быть, чтобы их библиотека работала так, как ожидалось, Когда вы используете auto, вы удаляете этот элемент управления от конструктора классов и вместо этого попросите компилятор выяснить, какой тип должен быть, который удаляет один из самых мощных инструментов С++ из панели инструментов и рискует нарушить их код.

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

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

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

Ответ 7

auto не имеет недостатков как таковых, и я защищаю его (вручную), используя его везде в новом коде. Это позволяет вашему коду последовательно проверять тип и последовательно избегать тихой нарезки. (Если B происходит от A, а функция, возвращающая A, внезапно возвращает B, тогда auto ведет себя как ожидалось, чтобы сохранить возвращаемое значение)

Хотя прежний код pre-С++ 11 может полагаться на неявные преобразования, вызванные использованием явно типизированных переменных. Изменение явно типизированной переменной до auto может изменить поведение кода, поэтому вам стоит быть осторожным.

Ответ 8

Ключевое слово auto просто выводит тип из возвращаемого значения. Следовательно, он не эквивалентен объекту Python, например.

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

Так как auto выводится во время компиляции, он не будет иметь никакого недостатка во время выполнения.

Ответ 9

То, о чем никто не упоминал до сих пор, но сам по себе заслуживает ответа, если вы спросите меня.

Так как (даже если все должны знать, что C != C++) код, написанный на C, может быть легко разработан для обеспечения базы для кода на С++ и поэтому должен быть разработан без особых усилий для совместимости с С++, это может быть требованием для дизайн.

Я знаю о некоторых правилах, где некоторые четко определенные конструкции из C являются недопустимыми для C++ и наоборот. Но это просто приведет к сломанным исполняемым файлам, и применяется известное предложение UB, которое в большинстве случаев замечено странными циклами, приводящими к сбоям или чем-либо (или даже может оставаться необнаруженным, но это не имеет значения здесь).

Но auto - это первый раз 1 это изменяется!

Представьте, что раньше вы использовали auto как спецификатор класса хранения и передавали код. Он даже не обязательно (в зависимости от того, как он использовался) "ломается"; он действительно мог бы тихо изменить поведение программы.

Что-то нужно иметь в виду.


1 По крайней мере, первый раз, когда я знаю.

Ответ 10

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

Ответ 11

Одна из причин, о которой я могу думать, заключается в том, что вы теряете возможность принуждать возвращаемый класс. Если ваша функция или метод вернули длинный 64-разрядный бит, и вам понадобилось только 32 неподписанных int, тогда вы потеряете возможность контролировать это.

Ответ 12

Другой раздражающий пример:

for (auto i = 0; i < s.size(); ++i)

генерирует предупреждение (comparison between signed and unsigned integer expressions [-Wsign-compare]), потому что i - это подписанный int. Чтобы этого избежать, вам нужно написать, например.

for (auto i = 0U; i < s.size(); ++i)

или, возможно, лучше:

for (auto i = 0ULL; i < s.size(); ++i)

Ответ 13

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

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Хорошее использование

итераторы

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Указатели функций

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Плохое использование

Поток данных

auto input = "";

..

auto output = test(input);

Подпись функции

auto test (auto a, auto b, auto c)
{
    ..
}

Тривиальные случаи

for(auto i = 0; i < 100; i++)
{
    ..
}

Ответ 14

я выбрал auto для значения размера и auto для iter в цикле for, и компилятор дал ошибку для сравнения этих двух значений. Я думал, что компилятор выбрал бы тот же самый тип, поскольку нам предлагается чаще использовать авто