Логический оператор XOR в C++?

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

// Detect when exactly one of A,B is equal to five.
return (A==5) ^^ (B==5);

, но нет ^^ оператора. Могу ли я использовать побитовое ^ здесь и получить правильный ответ (независимо от машинного представления true и false)? Я никогда не смешиваю & и && или | и ||, поэтому я не решаюсь сделать это с ^ и ^^.

Я бы более комфортно писать самостоятельно bool XOR(bool,bool) вместо функции.

11 ответов


на != оператор служит этой цели для bool значения.


для истинной логической операции XOR, это будет работать:

if(!A != !B) {
    // code here
}

причина, по которой мы отрицаем A и B преобразовать их в логические значения. Отрицание-это не намерение. Общий трюк для преобразования целых чисел в логические-использовать !!. Это преобразует значение переменной в 0 - это переменная 0 и 1 иначе. Мы могли бы использовать !!A != !!B вместо. Другой способ сделать использование (_Bool)A != (_Bool)B.


грамотное руководство логическое реализация XOR зависит от того, насколько близко вы хотите имитировать общее поведение других логических операторов (|| и &&) С вашим XOR. Есть две важные вещи об этих операторах: 1) они гарантируют оценку короткого замыкания, 2) они вводят точку последовательности, 3) они оценивают свои операнды только один раз.

оценка XOR, как вы понимаете, не может быть короткое замыкание, так как результат всегда зависит от обоих операнды. Итак, 1 вопрос. Но как насчет 2? Если вам все равно 2, то с нормализованным (т. е. bool) значений оператора != выполняет работу XOR с точки зрения результата. И операнды можно легко нормализовать с помощью unary !, если это необходимо. Таким образом !A != !B реализует правильный XOR в этом отношении.

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

a ? !b : b

это на самом деле так близко, как вы можете получить, чтобы сделать домашний XOR "похожим" на || и &&. Это будет работать, конечно, только если вы реализуете свой XOR как макрос. Функция не будет работать, так как последовательность не будет применяться к аргументам функции.

кто-то может сказать, что единственная причина наличия точки последовательности в каждом && и || поддержать короткозамкнутую оценку, и таким образом XOR не нуждается в нем. В этом есть смысл. Тем не менее, стоит рассмотреть возможность наличия XOR с точкой последовательности посередине. Например, следующее выражение

++x > 1 && x < 5

определил поведение и конкретный результат в C / C++ (по крайней мере, в отношении последовательности). Таким образом, можно разумно ожидать того же от определяемого пользователем логическое XOR, как в

XOR(++x > 1, x < 5)

а !=-based XOR не имеет этого свойства.


есть другой способ сделать XOR:

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

что, очевидно, можно продемонстрировать для работы через:

#include <iostream>

bool XOR(bool a, bool b)
{
    return (a + b) % 2;
}

int main()
{
    using namespace std;
    cout << "XOR(true, true):\t" << XOR(true, true) << endl
         << "XOR(true, false):\t" << XOR(true, false) << endl
         << "XOR(false, true):\t" << XOR(false, true) << endl
         << "XOR(false, false):\t" << XOR(false, false) << endl
         << "XOR(0, 0):\t\t" << XOR(0, 0) << endl
         << "XOR(1, 0):\t\t" << XOR(1, 0) << endl
         << "XOR(5, 0):\t\t" << XOR(5, 0) << endl
         << "XOR(20, 0):\t\t" << XOR(20, 0) << endl
         << "XOR(6, 6):\t\t" << XOR(5, 5) << endl
         << "XOR(5, 6):\t\t" << XOR(5, 6) << endl
         << "XOR(1, 1):\t\t" << XOR(1, 1) << endl;
    return 0;
}

оператор XOR не может быть закорочен; т. е. вы не можете предсказать результат выражения XOR, просто оценив его левый операнд. Таким образом, нет причин предоставлять ^^ версия.


был опубликован хороший код, который решил проблему лучше, чем !а != !б

обратите внимание, что мне пришлось добавить BOOL_DETAIL_OPEN / CLOSE, чтобы он работал на MSVC 2010

/* From: http://groups.google.com/group/comp.std.c++/msg/2ff60fa87e8b6aeb

   Proposed code    left-to-right?  sequence point?  bool args?  bool result?  ICE result?  Singular 'b'?
   --------------   --------------  ---------------  ---------- ------------  -----------  -------------
   a ^ b                  no              no             no          no           yes          yes
   a != b                 no              no             no          no           yes          yes
   (!a)!=(!b)             no              no             no          no           yes          yes
   my_xor_func(a,b)       no              no             yes         yes          no           yes
   a ? !b : b             yes             yes            no          no           yes          no
   a ? !b : !!b           yes             yes            no          no           yes          no
   [* see below]          yes             yes            yes         yes          yes          no
   (( a bool_xor b ))     yes             yes            yes         yes          yes          yes

   [* = a ? !static_cast<bool>(b) : static_cast<bool>(b)]

   But what is this funny "(( a bool_xor b ))"? Well, you can create some
   macros that allow you such a strange syntax. Note that the
   double-brackets are part of the syntax and cannot be removed! The set of
   three macros (plus two internal helper macros) also provides bool_and
   and bool_or. That given, what is it good for? We have && and || already,
   why do we need such a stupid syntax? Well, && and || can't guarantee
   that the arguments are converted to bool and that you get a bool result.
     Think "operator overloads". Here's how the macros look like:

   Note: BOOL_DETAIL_OPEN/CLOSE added to make it work on MSVC 2010
  */

#define BOOL_DETAIL_AND_HELPER(x) static_cast<bool>(x):false
#define BOOL_DETAIL_XOR_HELPER(x) !static_cast<bool>(x):static_cast<bool>(x)

#define BOOL_DETAIL_OPEN (
#define BOOL_DETAIL_CLOSE )

#define bool_and BOOL_DETAIL_CLOSE ? BOOL_DETAIL_AND_HELPER BOOL_DETAIL_OPEN
#define bool_or BOOL_DETAIL_CLOSE ? true:static_cast<bool> BOOL_DETAIL_OPEN
#define bool_xor BOOL_DETAIL_CLOSE ? BOOL_DETAIL_XOR_HELPER BOOL_DETAIL_OPEN

использовать простой:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));

вот как я думаю, вы пишете сравнение XOR на C++:

bool a = true;   // Test by changing to true or false
bool b = false;  // Test by changing to true or false
if (a == !b)     // THIS IS YOUR XOR comparison
{
    // do whatever
}

доказательство

XOR TABLE
 a   b  XOR
--- --- ---
 T   T   F
 T   F   T
 F   T   T
 F   F   F

a == !b TABLE
 a   b  !b  a == !b
--- --- --- -------
 T   T   F     F
 T   F   T     T
 F   T   F     T
 F   F   T     F

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

поэтому первоначальный вопрос заключается в том, как написать:

return (A==5) ^^ (B==5)

ответ

return (A==5) == !(B==5);

или, если хотите, напишите

return !(A==5) == (B==5);

(A || B) && !(A && B)

первая часть-это A или B, которая является включающей или; вторая часть-не A и B. вместе вы получаете A или B, но не оба A и B.

Это обеспечит XOR, доказанный в таблице истинности ниже.

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|

Я использую " xor "(кажется, это ключевое слово; в Код::Блоки по крайней мере он становится жирным) просто, как вы можете использовать "и" вместо && и "или" вместо ||.

if (first xor second)...

Да, это побитовое. Извиняюсь.


#if defined(__OBJC__)
    #define __bool BOOL
    #include <stdbool.h>
    #define __bool bool
#endif

static inline __bool xor(__bool a, __bool b)
{
    return (!a && b) || (a && !b);
}

он работает так, как определено. Условные обозначения должны определить, используете ли вы С, который просит BOOL вместо bool (длина отличается!)