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

Есть ли причина не использовать побитовые операторы &, |, и ^ для значений «bool» в C ++?

Иногда я сталкиваюсь с ситуациями, когда я хочу, чтобы одно из двух условий было истинным (XOR), поэтому я просто бросаю оператор ^ в условное выражение. Я также иногда хочу, чтобы все части условия были оценены, является ли результат истинным или нет (вместо короткого замыкания), поэтому я использую & и |. Мне также необходимо накапливать булевы значения, а & = и | = может быть весьма полезным.

При этом я сделал несколько поднятых бровей, но код по-прежнему значим и чище, чем в противном случае. Есть ли какая-нибудь причина НЕ использовать их для bools? Есть ли современные компиляторы, которые дают плохие результаты для этого?

|| и && – логические операторы, и встроенные гарантированно возвращают true или false . Ничего больше.

| , & и ^ – побитовые операторы. Когда область номеров, на которой вы работаете, равна 1 и 0, то они точно такие же, но в тех случаях, когда ваши логические значения не являются строго 1 и 0 – как в случае с языком C – вы можете столкнуться с каким-то поведением вы не хотели. Например:

 BOOL two = 2; BOOL one = 1; BOOL and = two & one; //and = 0 BOOL cand = two && one; //cand = 1 

В C ++, однако, тип bool гарантированно будет либо true либо false (который неявно конвертирует соответственно 1 и 0 ), поэтому это меньше беспокоит эту позицию, но тот факт, что люди не используются для видение таких вещей в кодексе является хорошим аргументом в пользу того, что вы этого не делаете. Просто скажите b = b && x и сделайте это.

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

Побитовое xor! = Логическое xor (кроме 0 и 1)

Во-первых, если вы работаете с значениями, отличными от false и true (или 0 и 1 , как целые числа), оператор ^ может ввести поведение, не эквивалентное логическому xor. Например:

 int one = 1; int two = 2; // bitwise xor if (one ^ two) { // executes because expression = 3 and any non-zero integer evaluates to true } // logical xor; more correctly would be coded as // if (bool(one) != bool(two)) // but spelled out to be explicit in the context of the problem if ((one && !two) || (!one && two)) { // does not execute b/c expression = ((true && false) || (false && true)) // which evaluates to false } 

Поблагодарите пользователя @Patrick за это.

Порядок операций

Во-вторых, | , & и ^ , как побитовые операторы, не замыкаются. Кроме того, множественные побитовые операторы, объединенные в один оператор, даже с явными скобками, могут быть переупорядочены путем оптимизации компиляторов, поскольку все 3 операции обычно являются коммутативными. Это важно, если порядок операций имеет значение.

Другими словами

 bool result = true; result = result && a() && b(); // will not call a() if result false, will not call b() if result or a() false 

не всегда даст тот же результат (или конечное состояние), что и

 bool result = true; result &= (a() & b()); // a() and b() both will be called, but not necessarily in that order in an // optimizing compiler 

Это особенно важно, потому что вы не можете контролировать методы a() и b() , или кто-то другой может появиться и изменить их позже, не понимая зависимости, и вызвать неприятную (и часто выпускающую только) ошибку.

я думаю

 a != b 

это то, что вы хотите

Приподнятые брови должны сказать вам достаточно, чтобы прекратить это делать. Вы не пишете код для компилятора, сначала пишете его для своих программистов, а затем для компилятора. Даже если компиляторы работают, удивление других людей не то, что вы хотите – побитовые операторы для бит-операций не для bools.
Полагаю, вы тоже кушаете яблоки с вилкой? Он работает, но он удивляет людей, поэтому лучше не делать этого.

Недостатки операторов bitlevel.

Ты спрашиваешь:

«Есть ли причина не использовать побитовые операторы & , | , и ^ для значений “bool” в C ++? »

Да, логические операторы , то есть встроенные булевы операторы высокого уровня ! , && и || , предлагают следующие преимущества:

  • Гарантированное преобразование аргументов в bool , то есть к 0 и 1 порядковым значениям.

  • Гарантированная оценка короткого замыкания, когда оценка expressии прекращается, как только конечный результат известен.
    Это можно интерпретировать как древовидную логику с True , False и Неопределенный .

  • Чтение текстовых эквивалентов not , и даже, даже если я сам их не использую.
    Как отмечает читатель Сурьмы в комментарии, у операторов bitlevel есть альтернативные токены, а именно bitand , bitor , xor и compl , но, на мой взгляд, они менее читаемы, чем and , or и not .

Проще говоря, каждое такое преимущество операторов высокого уровня является недостатком операторов bitlevel.

В частности, поскольку побитовые операторы не имеют преобразования аргументов в 0/1, вы получаете, например, 1 & 20 , а 1 && 2true . Также ^ , побитовое исключение или, может неправильно вести себя таким образом. Считается, что логические значения 1 и 2 одинаковы, а именно true , но рассматриваются как битпаттеры, они разные.


Как выразить логическое либо / или в C ++.

Затем вы предоставляете немного фона для вопроса,

«Иногда я сталкиваюсь с ситуациями, когда я хочу, чтобы одно из двух условий было истинным (XOR), поэтому я просто бросаю оператор ^ в условное выражение».

Ну, побитовые операторы имеют более высокий приоритет, чем логические операторы. Это означает, в частности, что в смешанном выражении, таком как

 a && b ^ c 

вы получите, возможно, неожиданный результат: a && (b ^ c) .

Вместо этого напишите просто

 (a && b) != c 

выражая более кратко, что вы имеете в виду.

Для множественного аргумента либо /, либо не существует оператора C ++, выполняющего задание. Например, если вы пишете a ^ b ^ c то это не выражение, которое говорит: «либо a , b либо c истинно». Вместо этого он говорит: «Нечетное число a , b и c истинно», которое может быть 1 из них или всего 3 …

Чтобы выразить общий либо / или когда a , b и c имеют тип bool , просто напишите

 (a + b + c) == 1 

или, с аргументами bool , преобразовать их в bool :

 (!!a + !!b + !!c) == 1 

Использование &= для накопления булевых результатов.

Вы уточняете,

«Мне также иногда нужно накапливать булевы значения, а &= и |=? может быть весьма полезным ».

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

Рассмотрим также:

 struct Bool { bool value; void operator&=( bool const v ) { value = value && v; } operator bool() const { return value; } }; #include  int main() { using namespace std; Bool a = {true}; a &= true || false; a &= 1234; cout << boolalpha << a << endl; bool b = {true}; b &= true || false; b &= 1234; cout << boolalpha << b << endl; } 

Вывод с Visual C ++ 11.0 и g ++ 4.7.1:

 правда
 ложный

Причина разницы в результатах заключается в том, что bitlevel &= не обеспечивает преобразование в bool его аргумента правой руки.

Итак, какой из этих результатов вы желаете для использования &= ?

Если первое, true , то лучше определить оператор (например, как указано выше) или именованную функцию или использовать явное преобразование выражения правой стороны или полностью написать обновление.

Вопреки ответу Патрика, у C ++ нет оператора ^^ для выполнения короткого замыкания или. Если вы подумаете об этом на секунду, наличие оператора ^^ любом случае не имеет смысла: с эксклюзивным или, результат всегда зависит от обоих операндов. Тем не менее, предупреждение Патрика о bool «булевых» типах одинаково хорошо сравнивается при сравнении 1 & 2 с 1 && 2 . Одним из classических примеров этого является функция GetMessage() Windows, которая возвращает три-состояние BOOL : отличное от нуля, 0 или -1 .

Использование & вместо && и | вместо || не является необычной опечаткой, поэтому, если вы намеренно это делаете, она заслуживает комментария, говорящего почему.

У Патрика были хорошие моменты, и я не собираюсь повторять их. Однако могу ли я предложить сокращение инструкций «if» на читаемом английском языке, где это возможно, с использованием хорошо известных логических варов. Например, и это использует логические операторы, но вы можете одинаково использовать поразрядные имена и соответственно называть bools:

 bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here bool onlyBIsTrue = (b && !a); // and not need this second line if (onlyAIsTrue || onlyBIsTrue) { .. stuff .. } 

Вы можете подумать, что использование логического значения кажется ненужным, но это помогает с двумя основными вещами:

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

EDIT: Вы явно не сказали, что хотите условные выражения для операторов if (хотя это кажется наиболее вероятным), это было мое предположение. Но мое предложение о промежуточном булевом значении все еще стоит.

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

Использование побитовой операции в выражении if будет служить одной и той же критике, хотя, возможно, не компилятором. Любое ненулевое значение считается истинным, поэтому что-то вроде «if (7 & 3)» будет истинным. Такое поведение может быть приемлемым в Perl, но C / C ++ – очень явные языки. Я думаю, что бровь Спока – должная осмотрительность. 🙂 Я бы добавил «== 0» или «! = 0», чтобы было совершенно ясно, какова была ваша цель.

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

Использование побитовых операций для bool помогает сэкономить ненужную логику outlookирования ветвлений процессором, в результате команды cmp, приводимой логическими операциями.

Замена логических побитовыми операциями (где все операнды – bool) генерирует более эффективный код, предлагающий тот же результат. Эффективность в идеале должна перевесить все преимущества короткого замыкания, которые могут быть использованы при упорядочении с использованием логических операций.

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

Interesting Posts

Как я могу форматировать номер строки, чтобы иметь запятые и круглые?

Как уменьшить время обновления в Outlook 2013 без удаления его папок / подпапок?

Как получить имя переменной с помощью отражения?

Получить имя и тип свойства, используя выражение lambda

Использование переменных внутри bash heredoc

Как конвертировать PDF-документ в PNG?

Как центрировать камеру так, чтобы маркер находился в нижней части экрана? (Google map api V2 Android)

Как получить панель инструментов из fragmentа?

В чем разница между lowagie и iText?

Подключить жесткий диск USB к беспроводному маршрутизатору на порте RJ45? Возможное?

ASP.NET MVC: пользовательская проверка по DataAnnotation

Как создать multidimensional array ArrayList в Java?

Могу ли я изменить Google Chrome, чтобы предсказать мой поиск, и мгновенно посетить первый результат?

Поиск временной зоны по широте долготы

Как избежать Не использовать stream приложений FX; currentThread = ошибка приложения JavaFX Application Thread?

Давайте будем гением компьютера.