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

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

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

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

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

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

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

108 голосов | спросил Brian Gianforcaro 30 +03002008-10-30T01:45:00+03:00312008bEurope/MoscowThu, 30 Oct 2008 01:45:00 +0300 2008, 01:45:00

14 ответов


0

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

ответил Don Neufeld 30 +03002008-10-30T01:47:07+03:00312008bEurope/MoscowThu, 30 Oct 2008 01:47:07 +0300 2008, 01:47:07
0

На самом деле это очень полезная идиома в некоторых контекстах. Возьмите эти макросы (пример из ядра 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 ++.

ответил Tom Barta 30 +03002008-10-30T07:50:27+03:00312008bEurope/MoscowThu, 30 Oct 2008 07:50:27 +0300 2008, 07:50:27
0

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

ответил fizzer 30 +03002008-10-30T02:00:47+03:00312008bEurope/MoscowThu, 30 Oct 2008 02:00:47 +0300 2008, 02:00:47
0

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

ответил jwfearn 30 +03002008-10-30T01:49:55+03:00312008bEurope/MoscowThu, 30 Oct 2008 01:49:55 +0300 2008, 01:49:55
0

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

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

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

ответил Richard Harrison 30 +03002008-10-30T01:56:27+03:00312008bEurope/MoscowThu, 30 Oct 2008 01:56:27 +0300 2008, 01:56:27
0

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

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

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

ответил RobH 31 +03002008-10-31T13:56:39+03:00312008bEurope/MoscowFri, 31 Oct 2008 13:56:39 +0300 2008, 13:56:39
0

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

ответил Marcin 30 +03002008-10-30T01:48:48+03:00312008bEurope/MoscowThu, 30 Oct 2008 01:48:48 +0300 2008, 01:48:48
0

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

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

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

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

ответил KarlU 3 WedEurope/Moscow2014-12-03T03:13:20+03:00Europe/Moscow12bEurope/MoscowWed, 03 Dec 2014 03:13:20 +0300 2014, 03:13:20
0

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

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

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

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

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

ответил Michael Burr 30 +03002008-10-30T02:08:24+03:00312008bEurope/MoscowThu, 30 Oct 2008 02:08:24 +0300 2008, 02:08:24
0

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

ответил Joshua 11 Maypm12 2012, 23:24:09
0

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


Пример проблемы:

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

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

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

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

Но в C ++ есть оператор !. Перегруженный !x обычно оценивает все биты x

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

ответил chux 26 AM00000030000001231 2014, 03:14:12
0

Это правильно, но в С здесь бессмысленно - «если» и «&» будет относиться к выражению так же, без '!!'.

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

ответил Darius Bacon 30 +03002008-10-30T02:02:55+03:00312008bEurope/MoscowThu, 30 Oct 2008 02:02:55 +0300 2008, 02:02:55
0

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

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

ответил dongilmore 30 +03002008-10-30T02:31:08+03:00312008bEurope/MoscowThu, 30 Oct 2008 02:31:08 +0300 2008, 02:31:08
0

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

В 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) - это то, что некоторые люди называют трюком с двойным ударом .

ответил sam 12 +03002016-10-12T17:53:46+03:00312016bEurope/MoscowWed, 12 Oct 2016 17:53:46 +0300 2016, 17:53:46

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132