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

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

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

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

Ответ 1

|| и && являются булевыми операторами, и встроенные гарантированно возвращают либо 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

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

Ответ 2

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

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

Ответ 3

Я думаю,

a != b

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

Ответ 4

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

Ответ 5

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

Вы спрашиваете:

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

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

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

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

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

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

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


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

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

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

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

a && b ^ c

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

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

(a && b) != c

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

Для множественного аргумента либо/или нет оператора С++, выполняющего задание. Например, если вы пишете a ^ b ^ c, чем это не выражение, которое говорит "либо a, b, либо c is true". Вместо этого он говорит: "Нечетное число a, b и c истинно", которое может быть 1 из них или всего 3 & hellip;

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

(a + b + c) == 1

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

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


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

Далее уточняйте,

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

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

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

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 С++ 11.0 и g++ 4.7.1:

true
false

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

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

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

Ответ 6

В отличие от ответа Патрика, у С++ нет оператора ^^ для выполнения короткого замыкания или. Если вы подумаете об этом на секунду, наличие оператора ^^ в любом случае не имеет смысла: с эксклюзивным или, результат всегда зависит от обоих операндов. Тем не менее, предупреждение Патрика о типах bool "Boolean" не одинаково хорошо при сравнении 1 & 2 с 1 && 2. Одним из классических примеров этого является функция

Ответ 7

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

Ответ 8

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

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

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