Почему мне нужно явно написать ключевое слово 'auto'? - PullRequest
0 голосов
/ 23 мая 2018

Я перехожу к C ++ 11 с C ++ 98 и уже знаком с ключевым словом auto.Мне было интересно, почему мы должны явно объявить auto, если компилятор может автоматически определить тип.Я знаю, что C ++ является строго типизированным языком, и это правило, но разве невозможно достичь того же результата без явного объявления переменной auto?

Ответы [ 7 ]

0 голосов
/ 24 мая 2018

Разве невозможно достичь того же результата без явного объявления переменной auto?

Я собираюсь немного перефразировать ваш вопрос таким образом, чтобы помочь вам понять, почемувам нужно auto:

Разве невозможно достичь того же результата без явного с использованием заполнителя типа ?

Разве это не возможно ?Конечно, это было «возможно».Вопрос в том, стоило ли бы это сделать.

Большинство синтаксисов в других языках, которые не имеют типовых имен, работают одним из двух способов.Есть Go-подобный способ, где name := value; объявляет переменную.И есть Python-подобный способ, где name = value; объявляет новую переменную, если name ранее не был объявлен.

Давайте предположим, что нет никаких синтаксических проблем с применением любого синтаксиса к C ++ (хотя яУже видно, что identifier, за которым следует : в C ++, означает «сделать ярлык»).Итак, что вы теряете по сравнению с заполнителями?

Ну, я больше не могу этого делать:

auto &name = get<0>(some_tuple);

Видите, auto всегда означает "значение".Если вы хотите получить ссылку, вам нужно явно использовать &.И он по ошибке не сможет скомпилироваться, если выражение присваивания является prvalue.Ни один из синтаксисов, основанных на присваивании, не имеет возможности различать ссылки и значения.

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

auto name = get<0>(some_tuple);

Это копирует из кортежа, создавая объект, независимый от some_tuple.Иногда это именно то, что вы хотите.Это еще более полезно, если вы хотите перейти от кортежа с помощью auto name = get<0>(std::move(some_tuple));.

ОК, поэтому, возможно, мы могли бы немного расширить эти синтаксисы, чтобы учесть это различие.Может быть, &name := value; или &name = value; означало бы выводить ссылку типа auto&.

ОК, хорошо.Как насчет этого:

decltype(auto) name = some_thing();

О, верно;C ++ на самом деле имеет два заполнителя: auto и decltype(auto).Основная идея этого вывода заключается в том, что он работает точно так же, как если бы вы сделали decltype(expr) name = expr;.Так что в нашем случае, если some_thing() является объектом, он выведет объект.Если some_thing() является ссылкой, она выведет ссылку.

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

Так что теперь нам нужно добавить больше к нашему синтаксису.name ::= value; означает «делать то, что decltype(auto) делает».У меня нет эквивалента для варианта Pythonic.

Глядя на этот синтаксис, не так ли легко случайно набрать неверно?Мало того, это вряд ли самодокументируется.Даже если вы никогда раньше не видели decltype(auto), он достаточно большой и очевидный, чтобы вы могли хотя бы легко сказать, что происходит что-то особенное.Принимая во внимание, что визуальная разница между ::= и := минимальна.

Но это вещи мнения;Есть более существенные вопросы.Видите, все это основано на использовании синтаксиса присваивания.Ну ... как насчет мест, где вы не можете использовать синтаксис присваивания?Вот так:

for(auto &x : container)

Меняем ли мы это на for(&x := container)?Потому что это, кажется, говорит что-то очень отличное от основанного на диапазоне for.Похоже, это оператор инициализатора из обычного цикла for, а не из диапазона for.Это также будет отличаться синтаксисом от невысказанных случаев.

Кроме того, инициализация копирования (с использованием =) не то же самое в C ++, что и прямая инициализация (использование синтаксиса конструктора).Таким образом, name := value; может не работать в случаях, когда auto name(value) имел бы.

Конечно, вы могли бы объявить, что := будет использовать прямую инициализацию, но это было бы совершенно несовместимо с тем, как остальныеC ++ ведет себя.

Также есть еще одна вещь: C ++ 14.Это дало нам одну полезную функцию вывода: вывод типа возврата.Но это основано на заполнителях.Так же, как основанный на диапазоне for, он основывается на типе имени, которое заполняется компилятором, а не на некотором синтаксисе, применяемом к определенному имени и выражению.

Видите, все эти проблемы возникаютиз того же источника: вы изобретаете совершенно новый синтаксис для объявления переменных.Объявления на основе заполнителей не должны были придумывать новый синтаксис .Они используют тот же синтаксис, что и раньше;они просто используют новое ключевое слово, которое действует как тип, но имеет особое значение.Это то, что позволяет ему работать на основе диапазона for и для вычета типа возврата.Это то, что позволяет ему иметь несколько форм (auto против decltype(auto)).И т. Д.

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

Если только это не было просто написание заполнителей с разными ключевыми словами или символами ...

0 голосов
/ 30 мая 2018

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

InВ языках с возможностью неявного определения переменных это может быть большой проблемой, особенно в больших системах.Вы делаете одну опечатку и отлаживаетесь только в течение нескольких часов, чтобы обнаружить, что вы непреднамеренно ввели переменную со значением ноль (или хуже) - blue против bleu, label против lable ... в результате выне может доверять никакому коду без тщательной проверки точных имен переменных.

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

Подумайте оЧтобы избежать подобных кошмаров, в ФОРТРАН было введено утверждение «неявное нет» - и вы видите, что оно используется во всех серьезных программах ФОРТРАНА в наши дни.Не иметь это просто ... страшно.

0 голосов
/ 23 мая 2018

Вкратце: auto может быть отброшено в некоторых случаях, но это может привести к несогласованности.

Прежде всего, как указано, синтаксис объявления в C ++ - <type> <varname>.Явные объявления требуют некоторого типа или, по крайней мере, ключевого слова объявления на его месте.Таким образом, мы могли бы использовать var <varname> или declare <varname> или что-то еще, но auto является давним ключевым словом в C ++ и хорошим кандидатом на ключевое слово автоматического вывода типа.

Возможно линеявно объявлять переменные путем присваивания, не нарушая все?

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

a = 0; // Error. Could be parsed as auto declaration instead.
int main() {
  return 0;
}

И когда дело доходит до любого вида локальных переменных, явные объявления являются способом управления областью действия переменной.

a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here

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

0 голосов
/ 23 мая 2018

Ваш вопрос допускает две интерпретации:

  • Зачем вообще нужен «авто»?Разве мы не можем просто бросить его?
  • Почему мы обязаны использовать авто?Разве мы не можем просто сделать это неявным, если оно не дано?

Вирсавия ответил приятно, первое толкование, для второго рассмотрим следующее (при условии, что других объявлений не существует)до сих пор; гипотетически допустимый C ++):

int f();
double g();

n = f(); // declares a new variable, type is int;
d = g(); // another new variable, type is double

if(n == d)
{
    n = 7; // reassigns n
    auto d = 2.0; // new d, shadowing the outer one
}

Это будет возможным, с другими языками вполне уместно (ну, кроме проблемы теневого копирования, возможно)) ... Однако в C ++ это не так, и вопрос (в смысле второй интерпретации) теперь таков: Почему?

На этот раз ответ не такочевидно как в первом толковании.Однако очевидно одно: явное требование к ключевому слову делает язык более безопасным (я не знаю, подтолкнуло ли это языковой комитет к его решению, но это все еще остается точкой):

grummel = f();

// ...

if(true)
{
    brummel = f();
  //^ uh, oh, a typo...
}

Можем ли мы согласиться с этим, не требуя каких-либо дальнейших объяснений?

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

(цитируется комментарий psmears ввиду его важности - спасибо за подсказку)

0 голосов
/ 23 мая 2018

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

  int x = some_function();

. Можно сделать более универсальным, сделав intтип автоматически выводится:

  auto x = some_function();

Так что это консервативное расширение языка;это вписывается в существующий синтаксис.Без этого x = some_function() становится оператором присваивания, больше не декларацией.

0 голосов
/ 23 мая 2018
Синтаксис

должен быть однозначным и обратно совместимым.

Если опустить auto, невозможно будет провести различие между утверждениями и определениями.

auto n = 0; // fine
n=0; // statememt, n is undefined.
0 голосов
/ 23 мая 2018

Удаление явного auto нарушило бы язык:

например,

int main()
{
    int n;
    {
        auto n = 0; // this shadows the outer n.
    }
}

, где вы можете видеть, что удаление auto не будет shadow theвнешний n.

...