Почему C ++ позволяет неявно преобразовывать арифметические или перечислимые значения в логические? - PullRequest
2 голосов
/ 19 июня 2009

C ++ стандарт говорит в разделе 4.12,

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

if(5)
  std::cout << "WOW!";

Это синтаксически допустимо, но семантически это не имеет никакого смысла. У меня вопрос, почему C ++ допускает такие странные вещи? AFAIK, это не дает никаких преимуществ, а не создает путаницу.

Ответы [ 9 ]

21 голосов
/ 19 июня 2009

Я думаю, это историческое явление, и побочный продукт того, как С использовал для оценки понятия типа bool ...

C не использовал булев тип, и в парадигме «вплоть до метала» C, указатели, числовые типы и т. Д., Которые были установлены в NULL, также были неявно false.

Если вы думаете об этом как «ничего / ноль == false» и «что-нибудь еще == true», это действительно имеет смысл.

7 голосов
/ 19 июня 2009

На самом деле, во многих языках, включая C / Python / Perl / BASIC ненулевое целое число / значение указателя всегда считается истинным, 0 считается ложным.

Это известные соглашения во многих языках программирования, поэтому нет причины, почему этого не должно быть в C ++?

Вопрос в том, почему в Java это не так?

3 голосов
/ 19 июня 2009

Это из продукта С.

Обратите внимание, что они (логически, если не вычислительно) эквивалентны:

if (x && y || z)

if (x * y + z)

(Я знаю это, потому что в TI99 / 4a BASIC не было ни операций И, ни ИЛИ, поэтому * и + пришлось выполнять двойную функцию, а в TI99 / 4a BASIC логические значения работали как в C.

2 голосов
/ 19 июня 2009

Я думаю, что причина в истории.Возможно, стоит отметить, что в следующей версии C ++ (C ++ 0x, C ++ 1x) введены перечисления областей, которые не допускаются для неявного преобразования в целое число, что делает следующий код некорректным

enum class X { A, B, C };
if(X::B) { ... }

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

if(static_cast<bool>(X::B)) { ... }

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

1 голос
/ 19 июня 2009

Это артефакт определенного аппаратного ограничения.

Термины "int", "short", "bool" и др. и др. имеют только реально различающееся семантическое значение, пока работает компилятор - во время выполнения это всего лишь 1 слово (8 бит), 2 слова (16 бит), 4 слова (32 бита) и т. д.

Таким образом, реальный вопрос здесь заключается не в том, «почему C ++, кроме 5, в качестве логического значения», а в «почему C?» И ответ заключается в том, что из-за выравнивания памяти было невозможно во всех случаях хранить один бит в любом месте, поэтому компиляторы просто использовали целое слово. Тот факт, что в C ++ есть обозначенный тип "bool", - это просто лексическое размахивание руками.

0 голосов
/ 21 июня 2009

Там есть хорошие ответы, но я хочу отметить еще одну вещь: даже если вы не можете сказать boolean = number и boolean = pointer, все же имеет смысл иметь конструкции if(number) или if(pointer).

0 голосов
/ 19 июня 2009

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

Обычно процессор не имеет булевых, целых и ptr-типов. Все они - просто числа в памяти или регистрах.

Лично мне всегда нравилась идиома:

some_obj * thingy;
if (thingy) ...

Кажется, что это естественный и лаконичный способ выражения "вещь существует" или "инициализирована". Но потом я провел много времени на ассемблере.

Хотя я склонен использовать "thingy == null" (или nil) в Objective-C, Java, C #, потому что вы просто чувствуете себя немного менее уверенным в том, что происходит в этих языках, вдали от оборудования. В некоторых языках кто-то мог даже переопределить оператор "==".

Вы могли бы просто думать о "if (ptr) ..." как о вызове неявного оператора "cast to boolean" ...

0 голосов
/ 19 июня 2009

Исторически C является относительно слабо типизированным языком , предназначенным для простоты и эффективности, а не для надежности. Паскаль и подобные языки более строго типизированы - перечисления, указатели, целые и bools нельзя произвольно смешивать и сопоставлять в выражениях.

C (и многие C ++) программисты выросли на этом фоне, и вы часто будете видеть идиоматический код, подобный этому:

while (1)
   dosomething(); // repeats forever

.. или ...

char * pointer;

if (pointer)  // tests pointer for being != 0, which equals NULL.
  fred = *pointer;

Лично, несмотря на использование C / C ++ на протяжении более 20 лет, я все еще нахожу эту вторую идиому уродливой, поскольку она делает два «скрытых» приведения / предположения в одной строке - та (NULL == 0) и та (0 == FALSE).

0 голосов
/ 19 июня 2009

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

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

Во-вторых, тестирование ненулевого результата в сборке, как правило, выполняется с использованием одного кода операции, который проверяет регистр на нулевое значение и, если оно истинно, с переходом к части оператора else. Если значение не равно нулю, выполнение естественным образом переходит в часть оператора if-true.

...