Двойное отрицание в коде С++

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

В основном я занимаюсь С++, и много кода, который они пишут, использует двойное отрицание для своей логической логики.

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

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

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

Является ли это правильным, или я что-то не хватает?

Ответ 1

Это трюк для преобразования в bool.

Ответ 2

На самом деле это очень полезная идиома в некоторых контекстах. Возьмите эти макросы (пример из ядра Linux). Для GCC они реализованы следующим образом:

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))

Почему они должны это делать? GCC __builtin_expect обрабатывает свои параметры как long, а не bool, поэтому должна быть какая-то форма преобразования. Поскольку они не знают, что такое cond, когда они пишут эти макросы, наиболее просто использовать !! idiom.

Они, вероятно, могли бы сделать то же самое, сравнивая с 0, но, на мой взгляд, на самом деле более просто сделать двойное отрицание, так как это самое близкое к cast-to-bool, которое имеет C.

Этот код может быть использован и на С++... это вещь с наименьшим общим знаменателем. Если возможно, выполните то, что работает как на C, так и на С++.

Ответ 3

Кодеры считают, что он преобразует операнд в bool, но поскольку операнды && уже неявно преобразуются в bool, это совершенно избыточно.

Ответ 4

Да, это правильно, и вы ничего не пропустили. !! - это преобразование в bool. Подробнее см. этот вопрос.

Ответ 5

Это метод, чтобы избежать записи (variable!= 0) - то есть для преобразования из любого типа, который он относится к bool.

ИМО-код, подобный этому, не имеет места в системах, которые необходимо поддерживать, потому что это неточно читаемый код (отсюда и вопрос в первую очередь).

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

Ответ 6

Это побочные действия для предупреждения компилятора. Попробуйте следующее:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

Строка "bar" генерирует "принудительное значение для bool" true "или" false "(предупреждение о производительности)" на MSVС++, но строка "baz" проходит через штраф.

Ответ 7

Является ли оператор! перегружен?
Если нет, они, вероятно, делают это, чтобы преобразовать переменную в bool без предупреждения. Это определенно не стандартный способ делать вещи.

Ответ 8

У разработчиков Legacy C не было Boolean-типа, поэтому они часто #define TRUE 1 и #define FALSE 0, а затем использовали произвольные числовые типы данных для булевых сравнений. Теперь, когда у нас есть bool, многие компиляторы будут выдавать предупреждения, когда определенные типы присвоений и сравнений производятся с использованием комбинации числовых типов и булевых типов. Эти два использования в конечном итоге столкнутся при работе с устаревшим кодом.

Чтобы обойти эту проблему, некоторые разработчики используют следующий логический идентификатор: !num_value возвращает bool true, если num_value == 0; false в противном случае. !!num_value возвращает bool false, если num_value == 0; true в противном случае. Единственного отрицания достаточно для преобразования num_value в bool; однако двойное отрицание необходимо для восстановления исходного смысла булевого выражения.

Этот шаблон известен как идиома, то есть что-то, обычно используемое людьми, знакомыми с языком. Поэтому я не вижу его как анти-шаблон, насколько я бы хотел static_cast<bool>(num_value). Листинг может очень хорошо дать правильные результаты, но некоторые компиляторы затем выдают предупреждение о производительности, поэтому вам все равно придется это решить.

Другим способом решения этого является, например, (num_value != FALSE). Я тоже в порядке, но в целом, !!num_value гораздо менее подробный, может быть более ясным и не путает второй раз, когда вы его видите.

Ответ 9

Как упоминалось Marcin, вполне возможно, что в игре будет перегрузка оператора. В противном случае в C/С++ это не имеет значения, если вы выполняете одно из следующих действий:

  • прямое сравнение с true (или в C чем-то вроде макроса true), что почти всегда плохое. Например:

    if (api.lookup("some-string") == true) {...}

  • вы просто хотите, чтобы что-то конвертировалось в строгое значение 0/1. В С++ присваивание bool будет делать это неявно (для тех вещей, которые неявно конвертируются в bool). В C или если вы имеете дело с переменной non-bool, это идиома, которую я видел, но я предпочитаю сорт (some_variable != 0) сам.

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

Ответ 10

Если переменная имеет тип объекта, она может иметь значение! оператор, но не сбрасывается в bool (или, что еще хуже, неявный приведение к int с другой семантикой. Вызов оператора! дважды приводит к преобразованию в bool, который работает даже в странных случаях.

Ответ 11

!! использовался, чтобы справиться с оригинальным С++, который не имел логического типа (как и C).


Пример задачи:

Внутри if(condition) значение condition должно оцениваться для некоторого типа типа double, int, void* и т.д., но не bool, поскольку он еще не существует.

Скажем, что существовал класс int256 (целое число 256 бит), и все целые преобразования/трансляции были перегружены.

int256 x = foo();
if (x) ...

Чтобы проверить, был ли x "истинным" или ненулевым, if (x) преобразует x в какое-то целое число, а затем оценит, был ли этот int отличным от нуля. Типичная перегрузка (int) x вернет только LSbits x. if (x) тогда тестировал только LSbits x.

Но С++ имеет оператор !. Перегруженный !x обычно оценивает все биты x. Итак, чтобы вернуться к неинвертированной логике, используется if (!!x).

Ref Использовали ли более старые версии С++ оператор` int` класса при оценке условия в операторе `if()`?

Ответ 12

Это правильно, но в C здесь бессмысленно - "if" и "& &" будет рассматривать выражение таким же образом без "!!".

Поводом для этого в С++, я полагаю, является то, что '& &' может быть перегружена. Но тогда, так можно "!", Поэтому на самом деле это не гарантирует, что вы получите bool, не глядя на код для типов variable и api.call. Может быть, кто-то, у кого больше опыта на C++, может объяснить; возможно, это означало в качестве меры защиты в глубину, а не гарантии.

Ответ 13

Возможно, программисты думали что-то вроде этого...

!! myAnswer является логическим. В контексте он должен стать логическим, но мне просто нравится бить вещи, чтобы убедиться, потому что когда-то была загадочная ошибка, которая укусила меня, и ударил, я убил ее.

Ответ 14

Это может быть пример трюка double-bang, см. The Safe Bool Idiom для получения более подробной информации. Здесь я резюмирую первую страницу статьи.

В С++ существует несколько способов предоставления булевых тестов для классов.

Очевидным способом является оператор преобразования operator bool.

// operator bool version
  class Testable {
    bool ok_;
  public:
    explicit Testable(bool b=true):ok_(b) {}

    operator bool() const { // use bool conversion operator
      return ok_;
    }
  };

Мы можем проверить класс,

Testable test;
  if (test) 
    std::cout << "Yes, test is working!\n";
  else 
    std::cout << "No, test is not working!\n";

Однако opereator bool считается небезопасным, поскольку он допускает бессмысленные операции, такие как test << 1; или int i=test.

Использование operator! безопаснее, потому что мы избегаем проблем с неявным преобразованием или перегрузкой.

Реализация тривиальна,

bool operator!() const { // use operator!
    return !ok_;
  }

Два идиоматических способа тестирования объекта Testable:

  Testable test;
  if (!!test) 
    std::cout << "Yes, test is working!\n";
  if (!test2) {
    std::cout << "No, test2 is not working!\n";

Первая версия if (!!test) - это то, что некоторые люди называют трюком double-bang.