C++ полагаться на неявное преобразование в bool в условиях?

Я нашел следующее правило в листе стандартов кодирования:

Не полагайтесь на неявное преобразование в bool в условиях.

if (ptr) // неправильно

if (ptr!= NULL) // нормально

Насколько разумно / полезно это правило?

Сколько перегрузок на скомпилированный код?

9 ответов

Решение

В самом строгом смысле вы можете положиться на неявные преобразования в bool. Обратная совместимость с Си требует этого.

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

Это правило ставит вас в тупик, если вы когда-нибудь захотите использовать класс, который имеет неявное преобразование в bool, такие как std::istream, Этот код читает слово из файла, пока не будет достигнут EOF:

std::ifstream file("foo.txt");
std::string word;

while (file >> word)
{
    // do stuff
}

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

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

Имея это в виду, вы должны абсолютно следовать стандарту кодирования при работе со встроенными типами (указатели, целые и т. Д.). Если вы столкнетесь с ситуацией, аналогичной приведенной выше, с классом, имеющим законное преобразование в boolЯ бы поднял вопрос с вашими товарищами по команде.

Я хотел добавить исторический взгляд на этот вопрос.

ANSI C (он же C89/C90) был первой формальной спецификацией для C. Наряду с K & R C, вы могли бы заметить нечто странное - понятия булева не существует. Операторы потока управления работают с выражениями, и их поток определяется в зависимости от того, оценивается ли выражение как 0 или нет. Отсутствие логического типа является одним из самых больших упущений в C. Только в C99 C получил _Bool тип и определенные макросы для bool, true, а также false в stdbool.h (обратите внимание, они все еще целые числа). Аналогично, C++ изначально не имел логического типа, получая его в C++98 с bool,

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

NULL это также просто макрос, равный нулю, поэтому, в частности, для указателей в C++11, вы должны использовать nullptr определить, является ли указатель нулевым.

if(ptr != nullptr)
{
    // Do something with ptr.
}

В большинстве случаев это не страшно, но может быть более читабельным, если вы введете именно то, что имеете в виду.

Иногда я думаю, что это правило может быть нарушено, например, при работе со стандартными библиотеками, которые возвращают int вместо bool (для совместимости с C89).

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

Мне очень не нравится это правило. В C++ нелогично использовать неявное преобразование в bool для типов указателей (и, конечно, для логических типов). ИМО, это намного легче читать

bool conditionMet = false;

while (!conditionMet) // read as "while condition is not met"
{
    /* do something */
}

чем читать это:

bool conditionMet = false;

while (conditionMet == false) // read as "while conditionMet is false"
{
    /* do something */
}

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

Это никак не повлияет на скомпилированный код.

Что касается того, насколько это полезно - это, безусловно, поможет пониманию людей, пришедших из таких языков, как Java/C#. Это сделает более явным то, что вы проверяете. Будет выдано предупреждение, если вы начнете сравнивать целые числа с NULL (и тем самым укажете, что вы не уверены в типе рассматриваемой переменной). Лично я предпочитаю первую форму, но это не совсем необоснованное требование.

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

Это впечатляюще глупое правило.

if (ptr != NULL) // ok

тогда почему бы и нет

 if ((ptr != NULL)==true) 
Другие вопросы по тегам