Использование побитовых операторов для булевых языков в С++
Есть ли причина не использовать побитовые операторы &, |, и ^ для значений "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 & 2
→ 0
, а 1 && 2
→ true
. Кроме того, ^
, побитовое исключение или, может неправильно вести себя таким образом. Считается, что логические значения 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", чтобы было совершенно ясно, какова была ваша цель.
Но в любом случае это звучит как личное предпочтение. Я бы запустил код через линт или аналогичный инструмент и посмотрел, не считает ли он это неразумной стратегией. Лично он читается как ошибка кодирования.