Использование побитовых операторов для логических значений в C ++ - PullRequest
65 голосов
/ 23 августа 2008

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

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

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

Ответы [ 9 ]

48 голосов
/ 24 августа 2008

|| и && являются логическими операторами, а встроенные гарантированно вернут либо true, либо false. Ничего другого.

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

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 и покончите с этим.

25 голосов
/ 29 августа 2008

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

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

11 голосов
/ 29 августа 2008

Я думаю

a != b

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

9 голосов
/ 24 августа 2008

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

6 голосов
/ 21 декабря 2012

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

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

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

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

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

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

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

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

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


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

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

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

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

a && b ^ c

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

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

(a && b) != c

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

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

».

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

(a + b + c) == 1

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

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


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

Вы уточните,

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

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

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

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

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

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

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

3 голосов
/ 16 сентября 2008

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

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

2 голосов
/ 24 августа 2008

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

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

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

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

0 голосов
/ 26 апреля 2018

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

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

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

0 голосов
/ 29 августа 2008

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

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

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

...