2008-10-29 4 views
100

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

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

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

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

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

Правильно ли это, или я что-то упускаю?

+0

Эта тема обсуждалась [здесь] (http://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c#206122). – Dima 2008-10-30 14:01:33

+4

проверьте здесь, уже спросил, [Is !! безопасный способ конвертировать в bool в C++?] (http://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c) – 2008-10-29 22:52:17

+0

Возможный дубликат [Is !! безопасный способ конвертировать в bool на C++?] (https://stackoverflow.com/questions/206106/is-a-safe-way-to-convert-to-bool-in-c) – EdChum 2017-12-04 16:46:34

ответ

104

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

+3

Точно так, это общая идиома в большинстве языков, где! является оператором отрицания с неявным преобразованием в boolean. – Gareth 2008-10-29 22:49:16

+13

Я думаю, что его явно использовать с (bool) было бы яснее, зачем использовать эту сложность !!, потому что она меньше печатает? – 2010-03-29 03:39:24

+21

Однако это бессмысленно на C++ или современном C, или где результат используется только в булевом выражении (как в вопросе). Это было полезно, когда у нас не было типа `bool`, чтобы избежать хранения значений, отличных от` 1` и `0` в логических переменных. – 2012-01-17 19:01:05

5

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

10

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

9

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

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

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

47

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

0

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

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

1

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

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

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

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

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

0

Может быть, программисты думали что-то вроде этого ...

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

64

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

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

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

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

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

8

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

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

Линия «бар» генерирует «принуждая значение BOOL„истинного“или„ложный“(предупреждение о производительности)» на MSVC++, но линия «Баз» пробирается через штраф.

1

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

1

!! использовался, чтобы справиться с оригинальным C++, который не имеет логического типа (как ни делали 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.

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

Ref Did older versions of C++ use the `int` operator of a class when evaluating the condition in an `if()` statement?

2

разработчики Наследство C не имели логический тип, поэтому они часто #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; однако двойное отрицание необходимо для восстановления исходного смысла булевого выражения.

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

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

0

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

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

Очевидным способом является 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) является то, что некоторые люди называют двойных взрыва трюком ,

Смежные вопросы