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


143

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

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


1
См. Также: stackoverflow.com/questions/6434971/… , stackoverflow.com/questions/15254461/… , stackoverflow.com/questions/6900459/… , stackoverflow.com/questions/8430053/is-c11-auto-type-dangerous и, возможно, другие. Не уверен, что это точные дубликаты, но они определенно кандидаты.
Коди Грей

3
Единственным недостатком, который я обнаружил, было то, что мне пришлось портировать кодовую базу на (консольную) платформу, компилятор которой не поддерживал (и не собирался поддерживать) функции C ++ 11!
Сэм

7
Просто для полноты ПОЛУЧИЛСЯ № 94 «Почти всегда авто»: herbutter.com/2013/08/12/…
Ричард Криттен

1
Я слушал cppcast и там было упоминание о ч / б авто и списочных инициализаторах. Я постараюсь найти этот подкаст.
Абхинав Гауниал,

2
я думаю, что первый недостаток влияет на читабельность кода
ggrr

Ответы:


111

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

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

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

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

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

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

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

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

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


58
Интересно отметить, что если такие примеры являются недостатком использования auto, то большинство утилитарных языков страдают таким недостатком по дизайну!
Лебен Аса

11
Если CallSomeFunction()возвращает другой тип в зависимости от последовательности его аргументов, это является недостатком дизайна CallSomeFunction(), а не проблемой auto. Если вы не читаете документацию по функции, которую используете, до ее использования, это проблема программиста, а не проблема auto. - Но я понимаю, что вы играете здесь адвоката дьявола, просто у Нира Фридмана гораздо лучше.
DevSolar

16
@DevSolar: Почему бы T CallSomeFunction(T, int, int)быть дефект дизайна? Очевидно, что он «возвращает другой тип в зависимости от последовательности своих аргументов».
MSalters

9
«Основной недостаток заключается в том, что при использовании autoвы не обязательно знаете тип создаваемого объекта». Можете ли вы объяснить, почему это проблема auto, а не проблема временных выражений подвыражения? Почему auto result = foo();плохо, а foo().bar()нет?
Angew больше не гордится SO

24
Из комментариев видно, что «недостаток» интерпретируется как причина того, что что-то недопустимо. Недостатком языковой функции является недостаток, который разработчик должен учитывать и обосновывать, принимать или нет - то есть делать инженерные компромиссы. Я не делаю общих заявлений о том, почему эту функцию следует или не следует использовать.
Питер

76

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

auto x = my_obj.method_that_returns_reference();

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

const auto& stuff = *func_that_returns_unique_ptr();

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

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


Почему вы можете глубоко скопировать дорогой объект для начала?
Лоран LA RIZZA

3
@LaurentLARIZZA: Некоторые классы имеют конструкторы копирования просто потому, что они иногда необходимы (например, экземпляры std::vector). Быть дорогостоящим для копирования - это свойство не класса, а отдельных объектов. Таким образом, method_that_returns_referenceможет относиться к объекту класса, который имеет конструктор копирования, но какой объект оказывается довольно дорогим для копирования (и не может быть перемещен из).
Марк ван Леувен

@MarcvanLeeuwen: Если объект дорогой для копирования и не может быть перемещен, почему он будет храниться в std::vector? (Потому что это может, да, или потому что вы не управляете классом, но это не главное) Если копирование дорого (и не имеет ресурса, потому что оно копируемое), почему бы не использовать COW для объекта? Локальность данных уже уничтожена размером объекта.
Лоран LA RIZZA

2
@LaurentLARIZZA Не экземпляр дорогостоящего экземпляра, хранящегося в векторе, просто копирование обычного, например, вектора <double>, это работа с кучей + O (N). Переезд это красная сельдь. Первая строка, которую я показал, будет копировать, а не перемещать, если только возвращаемая ссылка не является ссылкой-значением. COW действительно ни здесь, ни там. Дело в том, что дорогостоящее копирование объектов всегда будет существовать.
Нир Фридман

4
@Yakk Это не может безопасно сделать это, потому что это может нарезать. Единственная безопасная вещь, которую он может сделать - = deleteэто перегрузка. Хотя в целом то, что вы говорите, является решением. Это тема, которую я изучил, если вам интересно: nirfriedman.com/2016/01/18/… .
Нир Фридман

51

В других ответах упоминаются недостатки, такие как «вы на самом деле не знаете, какой тип переменной». Я бы сказал, что это во многом связано с соглашением о неаккуратном именовании в коде. Если ваши интерфейсы имеют четкие названия, вам не нужно беспокоиться о том, какой именно тип. Конечно, 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>, по всей вероятности, будет хранить const Matrix&и const Vector&внутренне; он ожидает, что он преобразуется в a 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
}

3
@NirFriedman Вы правы, это сильно, но я на самом деле чувствую, что у autoнего очень мало недостатков, поэтому я поддерживаю эту силу. И другие примеры прокси и т. Д. Включают в себя различные «строители строк» ​​и аналогичные объекты, найденные в DSL.
Angew больше не гордится SO

2
Я был укушен шаблонами выражений и autoпрежде, особенно с библиотекой Eigen. Это особенно сложно, потому что проблема часто не обнаруживается в отладочных сборках.
Дан

1
Использование autoможет также кусаться при использовании матричной библиотеки Armadillo , которая интенсивно использует шаблонное метапрограммирование для целей оптимизации. К счастью, разработчики добавили функцию .eval (), которую можно использовать, чтобы избежать проблем сauto
mtall

2
«Если ваши интерфейсы имеют четкие имена, вам не нужно заботиться о том, какой именно тип». Ваш компилятор не может проверить правильность вашего кода, изучая имена переменных. Это весь смысл системы типов. Слепо обходить это глупо!
Гонки легкости на орбите

1
@Angew: для временных пользователей это не проблема, потому что вы обычно сразу же используете их, что, autoкак правило, без какой-либо проверки типа (а разбрызгивание autoвезде снимает безопасность типов так же, как и везде). Это не очень хорошее сравнение.
Гонки легкости на орбите

13

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

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

3
Ну, вы получите iteratorв любом случае, так как ваша карта не const. если вы хотите преобразовать его в a const_iterator, либо явно укажите тип переменной как обычно, либо извлеките метод, чтобы ваша карта была константной в контексте вашей find. (Я бы предпочел последнее. SRP.)
Лоран LA RIZZA

auto city_it = static_cast<const auto&>(map).find("New York")? или, с C ++ 17, auto city_if = std::as_const(map).find("New York").
Dev Null

11

Это делает ваш код немного сложнее или утомительнее для чтения. Вообразите что-то подобное:

auto output = doSomethingWithData(variables);

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


40
Не всегда. auto it = vec.begin();намного легче читать, чем, std::vector<std::wstring>::iterator it = vec.begin();например.
Джонатан Поттер

4
Согласовано. Это зависит от варианта использования. Я мог бы быть более точным в этом.
Скам

1
@SeeDart да, люди, которые используют auto, делают это неправильно.
lciamp

6
«Отследить сигнатуру функции», если это не наведение мыши или нажатие клавиши («следовать символу» / «перейти к объявлению» / как бы оно ни называлось), вам необходимо либо настроить свой редактор больше, либо переключиться на IDE, которая может сделать это без настройки ... Однако ваша точка зрения остается в силе.
Гайд

6
Я заметил, что не в IDE, а в маленьких глазках различий при просмотре checkins! С авто они труднее читать по этой причине.
JDługosz

10

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

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

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

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

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

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


3
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should beНе очень хорошая причина ИМО. Современные IDE, например Visual Studio 2015, позволяют проверять тип переменной путем наведения курсора auto. Это * точно * так же, как typedefтот.
Сомбреро Цыпленок

@JameyD: Вы упускаете несколько важных моментов: (1) Аргумент IDE работает только в том случае, если тип конкретный, а не шаблонный. IDE не может сказать вам правильный тип в случае зависимых типов, например typename std::iterator_traits<It>::value_type. (2) Все дело в том , что выведенный тип потребности не быть «точно так же» , как правильный тип предписанию предыдущего дизайнера кода; используя auto, вы лишаете дизайнера возможности указать правильный тип.
user541686

Вы в основном говорите о прокси, который уже упоминается в одном из ответов. Шаблоны выражений и векторная ерунда не являются повседневным кодом для большинства людей. В большинстве случаев вам не нужны неявные преобразования, и в этом помогает auto. Херб Саттер подробно рассказывает о преимуществах auto в одном из своих постов в блоге, и речь идет не только о нажатиях клавиш, а также не только об общем коде. Кроме того, первая ссылка, которую вы указали, пост в блоге - просто ужасный совет (именно поэтому он громко критикуется в разделе комментариев).
Нир Фридман

@NirFriedman: "... vector<bool>ерунда" ... простите? Как вы думаете, bitsetэто реализовано? Или вы считаете битовые контейнеры бессмысленными?
user541686 15.01.16

1
@NirFriedman: Ничего о векторе <bool> для меня не новость. Я пытаюсь вам сказать, и вы явно отказываетесь понять, что для целей этого вопроса набор битов не отличается от вектора <bool> - оба они используют прокси, потому что прокси были сочтены полезными и тот факт, что прокси полезны, является реальностью, которую вы должны принять вместо того, чтобы жить в отрицании. Не могли бы вы прекратить превращать это в дискуссию о том, считаете ли вы, что прокси полезны? Это не предмет обсуждения, а также, ваше мнение о них - это просто ваше мнение, а не какой-то факт.
user541686 15.01.16

6

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

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


Даунтинг, это честно, но не могли бы вы прокомментировать, почему?
Лоран LA RIZZA

Я не отрицал вас, но autoесть недостатки как таковые (или, по крайней мере, многие так считают). Рассмотрим пример, приведенный во втором вопросе этой дискуссии с Саттером, Александреску и Мейерсом: если у вас есть auto x = foo(); if (x) { bar(); } else { baz(); }и foo()возвращается bool- что произойдет, еслиfoo() изменения вернут перечисление (три варианта вместо двух)? autoКод будет продолжать работать, но привести к неожиданным результатам.
einpoklum

@einpoklum: И использует ли bool вместо autoчего-либо что-либо в случае enum с незаданной областью? Я могу ошибаться (не могу проверить здесь), но я думаю, что единственное отличие состоит в том, что преобразование в boolпроисходит при объявлении переменной, а не при оценке условия в if. Если enumобласть действия ограничена, то преобразование в boolне должно происходить без явного уведомления.
Лоран LA RIZZA

4

Ключевое слово 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выводится во время компиляции, он не будет иметь никаких недостатков во время выполнения вообще.


1
да, он в основном делает то, что делает type()в Python. Он выводит тип, он не создает новую переменную этого типа.
lciamp

2
@lciamp На самом деле, это было бы decltype. autoспециально для назначения переменных.
Куб

4

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

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

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

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

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

Это то, что нужно иметь в виду.


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


1
В любом случае вы получите ошибку компилятора при попытке компиляции.
Цыпленок сомбреро

@JameyD: Что бы так сделать? почему 2 правильных кодовых ситуации с разным значением всегда приводят к ошибке?
16:45

8
Если вы полагаетесь на "ни один тип не подразумевает int » в C, вы заслуживаете всего плохого, что вы получите от этого. И если вы не полагаетесь на это, использование autoв качестве спецификатора класса хранения наряду с типом даст вам приятную ошибку компиляции в C ++ (что в данном случае хорошо).
Angew больше не гордится SO

1
@ Ага, хорошо, это тот случай, к которому я обращаюсь, да. Я не делаю этого. Но это то, что должно быть по крайней мере иметь в виду.
Дхейн

3

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


1
Есть static_cast, и IIRC, например, Meyers Effective Modern C ++ даже рекомендует использовать его для указания типа для автоматически вводимой переменной.
Гайд

2

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


2
Это не фанки. Вот что autoделает, никогда не выводя ни ссылку, ни constтип. Для autoсправки лучше использовать auto&&. (универсальная ссылка) Если тип не является дешевым для копирования или владеет ресурсом, то для начала тип не должен копироваться.
Лоран LA RIZZA

1

Еще один раздражающий пример:

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)

1
Да, меня это тоже раздражает. Но дыра в языке где-то еще. Чтобы этот код был действительно переносимым, предполагая sizeвозврат size_t, вы должны иметь возможность иметь size_tподобный литерал 0z. Но вы можете объявить UDL для этого. ( size_t operator""_z(...))
Лоран LA RIZZA

1
Чисто теоретические возражения: unsignedвполне вероятно, что он не будет достаточно большим, чтобы вместить все значения в std::size_tосновных архитектурах, поэтому в маловероятном случае, когда кто-то будет иметь контейнер с абсурдно гигантским количеством элементов, использование unsignedможет вызвать бесконечный цикл в нижнем диапазоне индексов. Хотя это вряд ли является проблемой, std::size_tследует использовать ее для получения чистого кода, который должным образом сигнализирует о намерениях. Я не уверен, что даже unsigned long longстрого гарантировано достаточно, хотя на практике это должно быть одинаково.
underscore_d

@underscore_d: да, честно говоря - unsigned long longгарантированно будет как минимум 64 бита, но в теории size_t, я думаю, может быть больше, чем это. Конечно, если в вашем контейнере> 2 ^ 64 элементов, у вас могут возникнуть более серьезные проблемы ... ;-)
Paul R

1

Я думаю, что 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++)
{
    ..
}

Когда вы хотите int, вы должны набрать еще один символ, если хотите auto. Это недопустимо
Рерито

@Rerito да, это intтак же легко увидеть здесь и печатать intкороче. Вот почему это тривиальный случай.
Халед.К

0

Я удивлен, что никто не упомянул об этом, но предположим, что вы вычисляете факториал чего-то:

#include <iostream>
using namespace std;

int main() {
    auto n = 40;
    auto factorial = 1;

    for(int i = 1; i <=n; ++i)
    {
        factorial *= i;
    }

    cout << "Factorial of " << n << " = " << factorial <<endl;   
    cout << "Size of factorial: " << sizeof(factorial) << endl; 
    return 0;
}

Этот код выведет это:

Factorial of 40 = 0
Size of factorial: 4

Это был определенно не ожидаемый результат. Это произошло потому, что autoвыводил тип переменной факториал так, как intбудто он был назначен 1.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.