Двойное отрицание в коде C ++ - PullRequest
109 голосов
/ 30 октября 2008

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

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

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

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

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

Это правильно, или я что-то упустил?

Ответы [ 14 ]

109 голосов
/ 30 октября 2008

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

69 голосов
/ 30 октября 2008

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

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

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

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

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

48 голосов
/ 30 октября 2008

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

11 голосов
/ 30 октября 2008

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

10 голосов
/ 30 октября 2008

Это метод, позволяющий избежать написания (переменная! = 0), т. Е. Конвертировать из любого типа в тип bool.

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

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

8 голосов
/ 31 октября 2008

Это обходит предупреждение компилятора. Попробуйте это:

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

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

5 голосов
/ 30 октября 2008

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

2 голосов
/ 03 декабря 2014

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

Чтобы обойти эту проблему, некоторые разработчики используют следующую логическую идентификацию: !num_value возвращает bool true if 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 гораздо менее многословен, может быть более ясным и не смущает, когда вы его видите во второй раз.

1 голос
/ 26 августа 2014

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


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

Внутри if(condition), condition необходимо вычислить для некоторого типа, например double, int, void* и т. Д., Но не для bool, поскольку он еще не существует.

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

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

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

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

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

1 голос
/ 11 мая 2012

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

...