Рассмотрим следующий код:
void foo(unsigned int x)
{
}
int main()
{
foo(-5);
return 0;
}
Компиляция без проблем. Подобные ошибки могут вызвать множество проблем и их трудно найти. Почему С++ допускает такое преобразование?
Рассмотрим следующий код:
void foo(unsigned int x)
{
}
int main()
{
foo(-5);
return 0;
}
Компиляция без проблем. Подобные ошибки могут вызвать множество проблем и их трудно найти. Почему С++ допускает такое преобразование?
Короткий ответ заключается в том, что C первоначально поддерживал такие преобразования, и они не хотели нарушать существующее программное обеспечение на С++.
Обратите внимание, что некоторые компиляторы будут предупреждать об этом. Например, g++ -Wconversion
будет предупреждать об этой конструкции.
Во многих случаях неявное преобразование полезно, например, когда int
использовался в вычислениях, но конечный результат никогда не будет отрицательным (известен из алгоритма и, возможно, утверждается).
EDIT: Дополнительное вероятное объяснение: Помните, что первоначально C был гораздо более слабым, чем С++. С объявлением функции стиля K & R было бы невозможно, чтобы компилятор обнаружил такие неявные преобразования, поэтому зачем ограничивать это на языке. Например, ваш код выглядел бы примерно так:
int foo(x)
unsigned int x
{
}
int main()
{
foo(-5);
return 0;
}
в то время как сама декларация была бы int foo(x);
Компилятор действительно полагался на программиста, чтобы передавать правильные типы в каждый вызов функции и не делал конверсий на сайте вызова. Затем, когда фактически вызванная функция, данные в стеке (и т.д.) Интерпретировались так, как указано в объявлении функции.
После того, как был написан код, основанный на таком неявном преобразовании, было бы намного сложнее удалить его из ANSI C, даже когда были добавлены прототипы функций с фактической информацией о типе. Вероятно, поэтому он остается в C даже сейчас. Затем появился С++ и снова решил не нарушать совместимость с C, продолжая допускать такие неявные преобразования.
Сделайте выбор.
@user168715 - это правильно. Первоначально С++ был разработан как супермножество C, притворяясь как можно более совместимым с обратной связью. Философия "С" заключается в том, чтобы доставить большую часть ответственности программисту, а не запрещать опасные вещи. Для программистов C это рай, для программистов на Java, это ад... вопрос вкуса.
Я буду копать стандарты, чтобы увидеть, где именно это написано, но у меня нет времени на это прямо сейчас. Я отредактирую свой ответ, как только смогу.
Я также согласен с тем, что некоторые унаследованные свободы могут привести к ошибкам, которые действительно трудно отлаживать, поэтому я добавляю к сказанному, что в g++ вы можете включить предупреждение, чтобы предотвратить такую ошибку: -Wconversion
.
Предупреждать о неявных преобразованиях, которые могут изменить значение. Это включает конверсии между реальным и целым числом, как abs (x), когда x является двойным; конверсии между подписанными и unsigned, как unsigned ui = -1; а также конверсии в меньшие типы, например sqrtf (M_PI). Не предупреждайте о явных отличает абс ((int) x) и ui = (без знака) -1, или если значение не изменено путем преобразования, как в abs (2.0). Предупреждения о конверсиях между целыми знаками без знака могут быть отключены с помощью -Wno-знак-преобразование.
Для С++ также предупреждайте о запутывании разрешения перегрузки для пользовательских преобразования; и конверсии, которые будут никогда не используйте оператор преобразования типов: конверсии в пустоту, один и тот же тип, базовый класс или ссылку на них. Предупреждения о конверсиях между целые числа без знака отключен по умолчанию в С++, если -Wsign-преобразование явно включено.
Другие компиляторы могут иметь одинаковые флаги.
К моменту первоначального стандарта C преобразование уже было разрешено многими (все?) компиляторами. Основываясь на обосновании C, как представляется, было мало (если есть) обсуждение вопроса о том, следует ли допускать такие неявные преобразования. К моменту появления С++ такие неявные преобразования были достаточно распространены, что устранение их привело бы к тому, что язык несовместим с большим количеством кода C. Вероятно, это сделало С++ чище; это, безусловно, сделало бы ее гораздо менее используемой - до такой степени, что она, вероятно, никогда не вышла бы за рамки "C с классами", и даже это будет просто игнорируемая сноска в истории лабораторий Bell.
Единственный реальный вопрос, стоящий на этой линии, заключался в том, что правила "сохранение значения" и "неподписанное сохранение" при продвижении значений без знака "меньше", чем int. Разница между двумя возникает, когда у вас есть (например) unsigned short, добавляемый к unsigned char.
Неподписанные правила сохранения говорят, что вы рекламируете оба беззнаковых int. Правила сохранения значений говорят, что вы рекламируете оба значения до int
, если оно может представлять все значения исходного типа (например, общий случай 8-разрядного char, 16-разрядного короткого и 32-битного int). С другой стороны, если int и short являются 16 битами, поэтому int не может представлять все значения unsigned short, тогда вы продвигаете unsigned short в unsigned int (обратите внимание, что он по-прежнему считается продвижением по службе, хотя это происходит только тогда, когда оно действительно а не продвижение - т.е. два типа имеют одинаковый размер).
К лучшему или худшему (и это неоднократно аргументировалось обоими направлениями) комитет выбирал сохранение ценности, а не беззнаковое сохранение рекламных акций. Обратите внимание, однако, что это имеет дело с преобразованием в противоположном направлении: вместо того, чтобы подписаться на unsigned, это о том, конвертируете ли вы без знака в подпись.
Поскольку стандарт допускает неявное преобразование из signed
в unsigned
.
Также (int)a + (unsigned)b
приводит к unsigned
- это стандарт С++.