Навіщо використовувати !! при перетворенні int на bool?


76

Що може бути причиною для перетворення цілого числа в логічне значення таким чином?

bool booleanValue = !!integerValue;

замість просто

bool booleanValue = integerValue;

Мені відомо лише те, що у VC ++ 7 остання спричинить попередження C4800, а перша - ні. Чи є якась інша різниця між ними?


2
Після подвійного заперечення значення гарантовано дорівнює 0 або 1; вихідним значенням може бути будь-яке значення int.
Мартін проти Левіса

1
Але чому друге твердження не буде робити точно так само?
гострий зуб

2
Я думаю, питання в тому, чому перший також не спричиняє C4800, оскільки навіть "Передача виразу типу bool не вимкне попередження, яке є задумом". (MS)
Тобіас,

2
чому більше людей не вгамовують, що це правильна відповідь. подвійний негатив дуже поширений у коді низького рівня
Matt Joiner

гм, насправді тут є своє місце ... люди плутають C ++ та C
Matt Joiner

Відповіді:


112

Проблеми з "!!" Ідіома полягає в тому, що це стисло, важко зрозуміти, його легко прийняти за друкарську помилку, легко кинути одне із знаків "!" тощо. Я помістив це в категорію "подивись, наскільки ми милі з C / C ++".

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


11
+1 для "bool isNonZero = (integerValue! = 0);". Я завжди ненавиджу, коли код поєднує ints, double, ptrs тощо з bool's. Вони бувають різних типів, і до них слід ставитись як до таких. Неявний привід буля до числового типу є анхронізмом.
jon-hanson

8
Я не бачу проблеми в тому, щоб розглядати ints / покажчики як bools - це ідіома C / C ++, і слід очікувати, що це знатиме будь-який розробник, який володіє ними. Однак іноді ви хочете уникнути попереджень компілятора і подібних - і в цих випадках чітке порівняння (або складання), безумовно, набагато переважніше порівняно незрозумілого !!трюку.
Павло Мінаєв

3
0 дорівнює false, тому що стандарт так говорить ;-) !! це досить поширена ідіома, з якою ви просто не знайомі. Це не робить його обов'язково поганим, за винятком використання на початку речення на природній мові.
Gunther Piez

16
+1 за "подивіться, якими милі ми можемо бути з C / C ++". Прекрасно підсумовує стільки поганих практик кодування, що дозволяють синтаксис C / C ++ та вільні правила.
Девід

8
-1 для "bool isNonZero = (integerValue! = 0);", це не є ні більш читабельним, ні більш "інтуїтивним". Якщо вам не подобаються стандартні ідіоми C / C ++, такі як !!, використовуйте іншу мову.
Гюнтер Пієз,

49

Історично !!ідіома використовувалась для того, щоб забезпечити, щоб ваш bool дійсно містив одне з двох значень, що очікуються у boolзмінній-подібній, оскільки C та C ++ не мали справжнього boolтипу, і ми підробили це за допомогою ints. Зараз це менше проблема з "справжніми"bool .

Але використання !!- це ефективний засіб документування (як для компілятора, так і для майбутніх людей, що працюють у вашому коді), що так, ви дійсно мали намір передати це intна a bool.


10
Я б не сказав "ефективний засіб документування", якби ОП навіть не розумів, що це означає. Набагато кращим способом буде static_cast <bool> (integerValue).
arolson101

10
ОП, мабуть, спочатку не знав, що це List<String>означає, або ||=. !!є дуже, дуже глибоко вкоріненою ідіомою у всесвіті С і С ++. Якщо хтось цього не знає, йому / їй слід це вивчити - а потім зробити власну обґрунтовану оцінку щодо того, чи використовувати його.
TJ Crowder,

@ arolson, @ TJ Crowder: !! може бути ефективним, але це неефективно
Lie Ryan

Чи може "справжній" бул мати більше двох значень? Наприклад, який результат (bool) 1 ^ (bool) 2 0 або 3?
dspyz

1
@ arolson101: static_cast <bool> (integerValue) - попередження у VSCPP 2015 (v14). Якщо застереження є помилкою для вас (або для вашого рецензента коду), вам не пощастило з цим ...
lorro

14

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

З моменту введення належного boolтипу в C ++ це більше не потрібно робити. Але ви не можете просто оновити всі застарілі джерела, і вам цього не потрібно робити, через зворотну сумісність C з C ++ (більшу частину часу). Але багато людей все ще роблять це з тієї ж причини: щоб залишати їх код назад сумісним зі старими компіляторами, які досі не розуміють bools.

І це єдина реальна відповідь. Інші відповіді оманливі.


13

Оскільки! IntegerValue означає integerValue == 0, а !! integerValue, таким чином, означає integerValue! = 0, дійсний вираз, що повертає bool. Останній - акторський склад з втратою інформації.


7

Інший варіант - тернарний оператор, який, здається, генерує на один рядок менше коду збірки (у 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

Буль може мати лише два стани, 0 і 1. Ціле число може мати будь-який стан від -2147483648 до 2147483647, якщо прийняти 32-бітове ціле число зі знаком. Унарний! оператор виводить 1, якщо вхід 0, і виводить 0, якщо вхід є чим-небудь, крім 0. Отже! 0 = 1 і! 234 = 0. Другий! просто перемикає вихід, таким чином 0 стає 1, а 1 стає 0.

Отже, перший вислів гарантує, що booleanValue буде встановлено рівним 0 або 1, і жодного іншого значення не буде, другий вираз - ні.


2
Це абсолютно неправильно. Друге твердження передбачає неявний int-> boolакторський склад. Це чітко визначено, і будь-яке ненульове значення intбуде перетворено в true, а 0 - в false. Попередження, яке дає VC ++, насправді є "попередженням про ефективність", і воно не має нічого спільного з цим.
Павло Мінаєв

Я думаю, ви плутаєте BOOL із bool. З bool ви отримуєте справжній bool із неявним складом, отже, істинним чи хибним.
Реджеп

2
Стани bool в C ++ - це не 1 і 0, а true і false.
Йоханнес Шауб - літб

Дякую, я цього не знав. Я думав, що 0 == false і 1 == true, але, мабуть, це не так.
Джон Сципіон,

ви мали рацію в перший раз, Джон Спійоне, тут є своє місце
Matt Joiner

4

!!це ідіоматичний спосіб перетворення 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]
> cl foo.cpp
foo.cpp
foo.cpp (6): попередження C4800: 'int': примусове значення до bool 'true' або 'false' (попередження про продуктивність)

[d: \ dev \ test]
> _

І принаймні одна людина вважає, що якщо зовсім новачок не визнає його значення, то це недобре. Ну це дурне. Існує багато такого, що зовсім не розуміють і не розуміють зовсім нові новачки. Написання власного коду для того, щоб його зрозумів будь-який початківець - це не те, що стосується професіоналів. Навіть для студентів. Починаючи з шляху виключення операторів та комбінацій операторів, які початківець не розпізнає ... Ну, у мене немає слів, щоб дати цьому підходу відповідний опис, вибачте.


1

Відповідь user143506 є правильною, але щодо можливої ​​проблеми продуктивності я порівняв можливості в asm:

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

test    edi/ecx, edi/ecx
setne   al
ret

Це було протестовано для GCC 7.1 та MSVC 19 2017. (Тільки boolean_converter у MSVC 19 2017 призводить до більшої кількості asm-коду, але це спричинено шаблонізацією та структурами і нею можна знехтувати з точки зору продуктивності, оскільки однакові рядки, як зазначено вище, можуть просто дублюватися для різних функцій з однаковим часом виконання.)

Це означає: різниці в продуктивності немає.

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);
}

-1

Немає жодної великої причини, за винятком того, що ви параноїк або кричите через код, що це негідність.

для компілятора врешті-решт це не буде мати значення.


Згенерований код цілком може бути "повільнішим": ціле число потребує тесту, щоб перевірити, чи дорівнює воно нулю. Однак для булевого значення, яке представляється як двійковий файл 1/1, тест не потрібен.
Йоханнес Шауб - літб

2
Але, добре, з цим раціональним ми повинні мати попередження про продуктивність для кожного виклику віртуальної функції ...
gimpf

-2

Мені ніколи не подобалася ця техніка перетворення на boolтип даних - вона пахне неправильно!

Натомість ми використовуємо зручний шаблон, який називається boolean_castзнайденим тут . Це гнучке рішення, яке є більш чітким у тому, що він робить, і може використовуватися наступним чином:

bool IsWindow = boolean_cast< bool >(::IsWindow(hWnd));

8
Це трохи надмірно, чи не так? Я маю на увазі в кінці дня перетворення на bool - це втрата точності і зводиться до чогось на зразок return b? true: false; Що приблизно те саме, що !! TRUE Особисто це надто інженерний STL-подібний фетиш.
Ігор Зевака

Я вважаю, ви не згодні з використанням boolean_cast тоді (отже, голос проти) - що б ви запропонували нам використовувати тоді?
Алан

5
Я пропоную вам використовувати мову такою, якою вона була призначена. Висока продуктивність і неприємність. Просто виріжте BS і виконайте роботу.
Matt Joiner
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.