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

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

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

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

23.08.2008 19:52:39
Если вы уже достаточно много узнали о C ++ после того, как задали этот вопрос, вы должны вернуться и не принять текущий ответ и принять ответ Патрика Джонмейера для правильного объяснения разницы в коротком замыкании.
codetaku 4.04.2017 14:50:36
Общее замечание, потому что люди задаются вопросом, если это хорошая идея. Если вы заботитесь о покрытии ветвями модульных тестов, то сокращение веток желательно. Битовые операторы полезны для этого.
qznc 22.05.2019 11:48:50
9 ОТВЕТОВ
РЕШЕНИЕ

||и &&являются логическими операторами, а встроенные гарантированно вернут либо либо, 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тип гарантированно будет либо a, trueлибо a false(что неявно преобразуется в соответственно 1и 0), так что это меньше беспокоит эту позицию, но тот факт, что люди не привыкли видеть такие вещи в коде дает хороший аргумент за то, что не делал этого. Просто скажи b = b && xи покончи с этим.

55
28.04.2016 09:30:43
нет такой вещи, как логический оператор XOR ^^ в C ++. Для обоих аргументов as bools! = Делает одно и то же, но может быть опасным, если любой из аргументов не является bool (как при использовании ^ для логического xor). Не уверен, как это было принято ...
Greg Rogers 6.10.2008 03:52:49
Оглядываясь назад, это было довольно глупо. В любом случае, изменили пример на &&, чтобы исправить проблему. Клянусь, я использовал ^^ когда-то в прошлом, но этого не должно было быть.
Patrick 7.10.2008 02:45:50
C имеет тип bool, который может быть 0 или 1 с 10 лет. _Bool
Johannes Schaub - litb 23.12.2008 00:54:53
Они по-прежнему не одинаковы даже для булов, потому что они не короткая циркуляция
aaronman 30.07.2013 20:44:36
Да, я твердо верю, что этот ответ не должен быть принят до тех пор, пока вы не измените «Когда домен чисел, с которыми вы работаете, равен 1 и 0, тогда они абсолютно одинаковы» на «Когда домен номеров, с которым вы работаете, равен 1 и 0, единственное отличие состоит в том, что побитовые операторы не закорачивают ". Первое утверждение категорически неверно, поэтому в то же время я отклонил этот ответ, поскольку он содержит это предложение.
codetaku 4.04.2017 14:45:50

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

10
23.08.2008 20:00:16
Да, это должен быть главный ответ. Следуйте принципу наименьшего удивления. Код, который делает необычные вещи, труднее читать и понимать, и код читается гораздо чаще, чем пишется. Не используйте такие хитрые трюки.
Eloff 26.04.2014 16:07:06
Я здесь, потому что мой коллега использовал побитовый оператор «и» для bool. И теперь я трачу свое время, пытаясь понять, правильно это или нет. Если бы он этого не делал, я бы сейчас занялся чем-нибудь более полезным -_-. Если вы цените время своих коллег, ПОЖАЛУЙСТА, не используйте побитовые операторы для bool!
anton_rh 21.12.2017 12:50:57

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

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

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

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

РЕДАКТИРОВАТЬ: Вы не сказали явно, что вы хотели условия для «если» заявления (хотя это кажется наиболее вероятным), это было мое предположение. Но мое предположение о промежуточном логическом значении все еще остается в силе.

2
24.08.2008 17:42:30

думаю

a != b

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

11
29.08.2008 01:56:49
Это правда, +1. Но нет никакой присваивающей версии этого оператора ( !==так сказать), поэтому, если вы вычисляете XOR последовательности boolзначений, вам нужно записать acc = acc!= condition(i);в теле цикла. Компилятор, вероятно, может обрабатывать это так же эффективно, как если бы он !==существовал, но некоторые могут найти, что он выглядит не очень хорошо, и предпочитают альтернативу добавления логических значений в виде целых чисел, а затем проверить, является ли сумма нечетной.
Marc van Leeuwen 28.04.2016 08:32:20

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

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

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

0
29.08.2008 02:08:12
Ваш пост побудил меня проверить это, а gcc не предупредил! Облом, потому что я собирался использовать это предупреждение, чтобы оправдать разрешение на запуск & = для накопления результатов bool, полагая, что другие увидят предупреждение, если позже они изменят мой тест. Мой код не работает даже для целых чисел, поскольку они оцениваются как 0 / false - без предупреждений! Время рефакторинга ...
sage 22.06.2013 18:37:41

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

Побитовый 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(), или кто-то другой может прийти и изменить их позже, не понимая зависимости, и вызвать неприятную (и часто только релиз-сборку) ошибку.

29
6.10.2008 19:03:59
Не совсем справедливое сравнение, если вы применяете bool в одном случае, а не в другом случае ... В обоих случаях приведение к bool - это то, что заставляет его работать, и почему оно хрупкое (потому что вы должны помнить это).
Greg Rogers 6.10.2008 14:31:36
Объяснение короткого замыкания состоит в том, почему это должен быть абсолютно принятый ответ; Ответ Патрика (ошибается ... другой Патрик, которого просто зовут Патрик) совершенно неверен, говоря: «Когда домен чисел, с которыми вы работаете, равен только 1 и 0, тогда они абсолютно одинаковы»
codetaku 4.04.2017 14:47:10

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

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

3
16.09.2008 05:54:41
^^Оператор должен еще быть полезным , независимо от рассмотрения короткого замыкания. Было бы а) оценить операнды в логическом контексте и б) гарантированно вернуть 1 или 0.
Craig McQueen 21.02.2011 00:37:15
@CraigMcQueen. Это просто определить inline bool XOR(bool a,bool b) { return a!=b; }и получить то, что вы хотите, за исключением того, что это функция, а не (инфиксный) оператор. Или вы можете напрямую использовать !=или перегрузить какой-либо другой оператор с этим значением, но тогда, конечно, вы должны быть очень осторожны, чтобы случайно не использовать непреднамеренную перегрузку с тем же именем. А между прочим ||и &&вернуть trueили falseнет, 1или нет 0.
Marc van Leeuwen 28.04.2016 08:50:02
И мне кажется , что тот факт , что !, ||и &&оценить их аргументы в логическом контексте только потому , что эти операторы редко перегружены принимать другие типы; насколько я могу судить, язык допускает такие перегрузки и, следовательно, не гарантирует оценку аргументов в булевом контексте.
Marc van Leeuwen 28.04.2016 09:13:53

Недостатки операторов битового уровня.

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

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

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

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

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

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

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

В частности, поскольку у побитовых операторов отсутствует преобразование аргумента в 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 <iostream>

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:

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

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

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

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

7
23.05.2014 12:19:59
побитовые операторы также имеют текстовые эквиваленты
Antimony 23.05.2014 09:31:14
@Antimony: Я не понял , что комментарий на первый , но да, операции bitlevel альтернативные маркеры bitand, bitor, xorи compl. Я думаю, именно поэтому я использовал квалификацию «читабельный». Конечно, читабельность, например compl 42, субъективна. ;-)
Cheers and hth. - Alf 23.05.2014 11:29:20
Насколько я могу судить, логические операторы могут быть перегружены другими типами аргументов (хотя они обычно таковыми не являются), поэтому первый пункт «гарантированное преобразование аргументов в bool» является лишь следствием соглашения, а не гарантией того, что язык C ++ фактически дает вы.
Marc van Leeuwen 28.04.2016 09:18:44
@MarcvanLeeuwen: Возможно, ваша визуальная подсистема не смогла заметить «встроенные логические операторы высокого уровня!, && и ||». Вы правы в том, что эти операторы могут быть перегружены, и тогда основная проблема заключается в том, что семантика меняется незначительно, а не оценка короткого замыкания. Но это проблема перегрузок, а не встроенных операторов.
Cheers and hth. - Alf 28.04.2016 09:25:01
Это верно (я скучал по этому). Но тогда это утверждение все еще вводит в заблуждение, потому что вы просто говорите, что эти операторы принимают логические аргументы; если (и только если) будут выбраны встроенные версии операторов, то компилятор вставит преобразование аргументов. Аналогично, встроенный (высокий уровень) оператор сложения целых чисел без знака +гарантирует преобразование своих аргументов в unsigned, но это не мешает unsigned int n=-1; n+=3.14;фактически использовать double(или это float?) Операцию сложения. (Сравните ваш &=пример.)
Marc van Leeuwen 28.04.2016 09:47:44

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

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

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

-1
26.04.2018 09:34:32