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


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

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

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

9 61

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 или 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++? "

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

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

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

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

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

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


как выразить логическими или в 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:

true
false

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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