Зачем использовать !! при конвертации int в bool? - PullRequest
76 голосов
/ 21 августа 2009

Что может быть причиной для преобразования целого числа в логическое значение таким образом?

bool booleanValue = !!integerValue;

вместо

bool booleanValue = integerValue;

Все, что я знаю, это то, что в VC ++ 7 последний вызовет C4800 предупреждение , а первый - нет. Есть ли какая-то другая разница между ними?

Ответы [ 10 ]

111 голосов
/ 21 августа 2009

Проблемы с "!!" идиома в том, что это кратко, трудно увидеть, легко принять за опечатку, легко отбросить один из «!» и так далее. Я поместил это в категорию «посмотри, как мило мы можем быть с C / C ++».

Просто напишите bool isNonZero = (integerValue != 0); ... будьте ясны.

49 голосов
/ 21 августа 2009

Исторически идиома !! использовалась, чтобы гарантировать, что ваш bool действительно содержал одно из двух значений, ожидаемых в bool -подобной переменной, потому что C и C ++ не имели истинного типа bool, и мы подделал его с int с. Теперь это не проблема с «реальными» bool s.

Но использование !! является эффективным средством документирования (как для компилятора, так и для всех будущих людей, работающих над вашим кодом), что да, вы действительно намеревались привести это int к bool.

14 голосов
/ 28 августа 2013

Используется, потому что язык C (и некоторые предстандартные компиляторы C ++ тоже) не имели типа bool, просто int. Таким образом, int были использованы для представления логических значений: 0 должен был означать false, а все остальное было true. Оператор ! возвращал 1 из 0 и 0 из всего остального. Двойной ! был использован для их инвертирования, и он был там, чтобы убедиться, что значение равно 0 или 1 в зависимости от его логического значения.

В C ++, начиная с введения правильного типа bool, больше нет необходимости делать это. Но вы не можете просто обновить все устаревшие источники, и вам не нужно этого делать из-за обратной совместимости C с C ++ (большую часть времени). Но многие люди все еще делают это по той же причине: сохранять свой код обратно совместимым со старыми компиляторами, которые до сих пор не понимают bool s.

И это единственный реальный ответ. Другие ответы вводят в заблуждение.

13 голосов
/ 21 августа 2009

Поскольку! IntegerValue означает integerValue == 0, а !! integerValue, таким образом, означает integerValue! = 0, допустимое выражение, возвращающее значение типа bool. Последний является актером с потерей информации.

6 голосов
/ 12 сентября 2013

Другим вариантом является троичный оператор, который, по-видимому, генерирует на одну строку меньше ассемблерного кода (в любом случае в Visual Studio 2005):

bool ternary_test = ( int_val == 0 ) ? false : true;

, который выдает код сборки:

cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _ternary_test$[ebp], al

Против:

bool not_equal_test = ( int_val != 0 );

, который производит:

xor eax, eax
cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _not_equal_test$[ebp], al

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

5 голосов
/ 21 августа 2009

bool может иметь только два состояния, 0 и 1. Целое число может иметь любое состояние от -2147483648 до 2147483647, если принять 32-разрядное целое число со знаком. Одинарный! оператор выводит 1, если на входе 0, и выводит 0, если на входе ничего кроме 0. Итак! 0 = 1 и! 234 = 0. Второе! просто переключает выход, так что 0 становится 1, а 1 становится 0.

Таким образом, первый оператор гарантирует, что booleanValue будет установлен равным либо 0, либо 1, и никакого другого значения, второй оператор - нет.

4 голосов
/ 19 ноября 2011

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

Из других ответов и комментариев я вижу, что многие люди не знакомы с полезностью этой идиомы в программировании Windows. Это означает, что они не занимались серьезным программированием для Windows. И предположим вслепую, что то, с чем они столкнулись, является репрезентативным (это не так).

#include <iostream>
using namespace std;

int main( int argc, char* argv[] )
{
    bool const b = static_cast< bool >( argc );
    (void) argv;
    (void) b;
}
> [d:\dev\test]
> <b>cl foo.cpp</b>
foo.cpp
foo.cpp(6) : warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)

[d:\dev\test]
> _

И, по крайней мере, один человек думает, что если начинающий новичок не осознает его значение, то это плохо. Ну, это глупо. Многие новички не узнают и не понимают. Написание своего кода, чтобы его мог понять любой начинающий, не является чем-то для профессионалов. Даже для студентов. Начиная с пути исключения операторов и комбинаций операторов, которые не распознают новички ... Ну, у меня нет слов, чтобы дать этому подходу подходящее описание, извините.

Приветствия & hth.,

1 голос
/ 27 ноября 2017

Ответ пользователя 143506 правильный, но для возможной проблемы с производительностью я сравнил возможности в asm:

return x;, return x != 0;, return !!x; и даже return boolean_cast<bool>(x) приводит к этому идеальному набору инструкций asm:

test    edi/ecx, edi/ecx
setne   al
ret

Это было проверено на GCC 7.1 и MSVC 19 2017. (Только boolean_converter в MSVC 19 2017 приводит к большему количеству асм-кода, но это вызвано шаблонизацией и структурами и может игнорироваться с точки зрения производительности, потому что те же строки, что указаны выше, могут просто дублироваться для разных функций с одинаковым временем выполнения.)

Это означает: разницы в производительности нет.

PS: этот boolean_cast использовался:

#define BOOL int
// primary template
template< class TargetT, class SourceT >
struct boolean_converter;

// full specialization
template< >
struct boolean_converter<bool, BOOL>
{
  static bool convert(BOOL b)
  {
    return b ? true : false;
  }
};

// Type your code here, or load an example.
template< class TargetT, class SourceT >
TargetT boolean_cast(SourceT b)
{
  typedef boolean_converter<TargetT, SourceT> converter_t;
  return converter_t::convert(b);
}

bool is_non_zero(int x) {
   return boolean_cast< bool >(x);
}
0 голосов
/ 21 августа 2009

Мне никогда не нравился этот метод преобразования в тип данных bool - он пахнет неправильно!

Вместо этого мы используем удобный шаблон с именем boolean_cast found here . Это гибкое решение, более четкое в том, что оно делает, и его можно использовать следующим образом:

bool IsWindow = boolean_cast< bool >(::IsWindow(hWnd));
0 голосов
/ 21 августа 2009

Нет большой причины, кроме как быть параноиком или кричать через код, что это глупость.

для компилятора, в конце концов, это не будет иметь значения.

...