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

292

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

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

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

bool XOR(bool,bool)Вместо этого мне было бы удобнее написать свою собственную функцию.

RAC
источник
57
На самом деле, Джим, это не единственная разница между & и &&, например ... 1 && 2 - True. но 1 & 2 => 0. Из-за этого я думаю, что «короткое замыкание» - это просто свойство, которое они имеют. Логика является более важной особенностью ...
Брайан Постоу
6
Не говоря уже о том, что 2 && 3 == верно, но 2 & 3 == 2.
Дэвид Торнли
1
Дэвид Томли: Хорошо, да, но 2 ==> правда, так что все в порядке ... Помните, что действительно нет никаких логических значений ...
Брайан Постоу
13
@BrianPostow: На самом деле, в C ++, есть.
Адриан Вилленбюхер
7
Как указано ниже, вот ответ Денниса Ричи о том, почему его не существует: c-faq.com/misc/xor.dmr.html
Tobia

Ответы:

536

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

Грег Хьюгилл
источник
7
Но ложь! = Ложь => ложь
Джонас
13
Обратите внимание, что это работает только для логических значений. И ^ будет прекрасно работать там. 2! = 1 => 1, что не то, что вы хотите! как говорит ЛираНуна, вставляю! Infront обеих сторон решает эту проблему. но опять же, тогда вы можете использовать поразрядно ^ ...
Брайан Постоу
8
Правильно, я осторожно упомянул «для boolзначений», потому что это не обязательно делает то, что вы могли бы хотеть для небулевых. И поскольку это C ++, существует реальный boolтип, вместо того, чтобы использовать его intдля этой цели.
Грег Хьюгилл
29
Если вы хотите сделать это для типа, aпросто напишите!(a) != !(a)
Крис Латс
6
@ChrisLutz: да, но остерегайтесь перегруженных операторов.
Руон
246

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

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

Обратите внимание, что !здесь есть для преобразования значений в булевы значения и их отрицания, так что два неравных положительных целых числа (каждое а true) будут иметь значение false.

LiraNuna
источник
6
Я не понимаю, почему А и В отрицаются с!
Мартин Хансен,
26
Главным образом, чтобы преобразовать их в логическое значение. !!будет работать, просто спросите хорошо, но так как они должны отличаться, отрицание их не вредит.
LiraNuna
4
Вопрос в том, смогут ли компиляторы правильно оптимизировать это.
einpoklum
6
Незнание важности нормализации bools стоило мне 2 дня.
Макан Тайеби
9
@LiraNuna, "почему А и Б отрицаются!" / "Главным образом, чтобы преобразовать их в логическое значение." - Думаю, это стоит упомянуть в ответе.
cp.engr
42

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

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

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

a ? !b : b

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

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

++x > 1 && x < 5

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

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

в то время как !=XOR на основе этого свойства не имеет.

Муравей
источник
1
Вы упускаете другую важную вещь ||и &&: C) они оценивают операнды в логическом контексте. То есть 1 && 2это правда, в отличие от 1 & 2которого равен нулю. Аналогичным образом, ^^оператор может быть полезен для предоставления этой дополнительной функции оценки операндов в логическом контексте. Например 1 ^^ 2, ложно (в отличие от 1 ^ 2).
Крейг МакКуин
2
@Craig McQueen: я не скучаю по этому. Второй абзац моего поста упоминает об этом. По моему мнению, обработка операндов как логических значений не является критической особенностью логических операторов, в том смысле, что они не будут введены по одной только этой причине. Основной причиной, по которой они были введены, является короткозамкнутая оценка и требуемая для этого точка последовательности.
AnT
В настоящее время ваше предложение будет работать только с макросом? Хотя верно то, что порядок параметров, которые должны оцениваться в функции, зависит от компилятора, не редкость ли в настоящее время разница слева направо? Кроме того, здесь, в комментариях, стоит отметить, что если реализация выглядит как #define XOR(ll,rr) { ll ? !rr : rr }, то вызов like int x = 2; XOR(++x > 1, x < 5);даст неправильный результат. Вызов должен иметь дополнительные скобки, как в int x = 2; XOR( (++x > 1), (x < 5) );, чтобы дать правильный ожидаемый результат.
РА
1
Поскольку XOR не может замкнуть накоротко, нет необходимости в точке последовательности. XOR больше похож на + в этом отношении. Если вы также не хотите утверждать, что (++ x) + x равно 2x + 1, точка последовательности не является разумной.
hkBst
1
@hkBst: я думаю, что это полностью освещено во второй части моего ответа.
AnT
25

Есть еще один способ сделать 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;
}
Берти Уин
источник
7
Этот подход может генерировать довольно медленный код - в 3-5 раз медленнее, чем (! A)! = (! B) на clang и gcc с использованием libstdc ++: quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon
18

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

Мехрдад Афшари
источник
25
-1 потому что основное различие между && и & не только в коротком замыкании. 1 && 2 - Истина, но 1 & 2 - Ложь. Короткое замыкание - просто удобный побочный эффект.
Брайан Постоу
6
Ответ не о чем &&и &вообще. Дело в том, что нет причин для введения ^^. Я подозреваю, что свойство, ^^которое рассматривает любое ненулевое значение, 1не очень полезно. Или, по крайней мере, я не вижу никакой пользы.
Йоханнес Шауб -
6
Также C ++! = Другие языки. В C и C ++, как показано выше, короткое замыкание - это не просто что-то приятное, но принципиально важное. :)
Йоханнес Шауб -
13
Тогда вам следует прочитать ответ Денниса Ричи о том, почему его не существует: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Йоханнес Шауб - litb
5
Вот рабочая ссылка на ответ Денниса Ричи о том, почему он не существует: c-faq.com/misc/xor.dmr.html
Tobia
13

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

Обратите внимание, что мне пришлось добавить 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
элегантные кости
источник
6

Используйте простое:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
tiands
источник
5

Вот как я думаю, что вы пишете сравнение 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);
Indinfer
источник
! a! =! b кажется более приятным, потому что он преобразует ваши аргументы в bool для вас.
xaxxon
3

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

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

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

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|
Ешь в Джо
источник
Я не копаю
xaxxon
Там нет необходимости защищаться об этом.
xaxxon
1
@xaxxon: Не вижу смысла в этом тесте !? В StringCreation вы использовали для создания строки, но во втором тесте вы этого не сделали. Если вы поместите идентичный код в оба теста и вызовете разные XOR для каждого теста (XOR и XOR2), вы получите те же результаты теста. Так что ты пытался сказать?
SoLaR
1

Я использую «xor» (кажется, это ключевое слово; по крайней мере, в Code :: Blocks оно выделяется жирным шрифтом) так же, как вы можете использовать «и» вместо &&и «или» вместо ||.

if (first xor second)...

Да, это побитовое. Сожалею.

csiz
источник
2
Я предполагаю, что это скрытые #defines откуда-то. Я почти уверен, что "and" и "xor" не являются ключевыми словами в ansi C ... (по крайней мере, не C79)
Брайан Постоу
1
@Brian Postow: Я не знаю, что такое C79, но в C ++ 98 andи xorэто стандартные макросы библиотеки. Они не откуда-то, они из <iso646.h>. Эти макросы также есть в C99 (не уверен насчет C89 / 90).
AnT
5
@ Брайан Postow: ... xorозначает побитового исключающего , хотя, в то время как andэто логично и.
AnT
1
Я опечатал C89 как C79 ... и xor и т. Д. Нет в моей копии K & R. Я не думаю, что когда-либо использовал iso686.h, по крайней мере, не сознательно ... и так, да, они #defines откуда-то, вы просто знаете, где это где-то B-)
Брайан Постоу
2
Они, конечно, в C99, используя этот заголовок. В C ++ они интегрированы в язык как «альтернативные токены», и вы можете struct A { compl A() { } };, например, определить деструктор.
Йоханнес Шауб -
0
#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);
}

Это работает как определено. Условия должны определить, используете ли вы Objective-C , который запрашивает BOOL вместо bool (длина другая!)

Макстон Чан
источник
3
Это нарушает правило двойного подчеркивания.
Тамас Селеи
@ TamásSzelei Не обязательно, поскольку компилятор не видит этого, поскольку он предварительно обработан, и в мире Objective-C двойные подчеркивания довольно распространены.
Maxthon Chan
Хорошая мысль о препроцессоре, хотя для меня это все еще запах кода таким образом (зачем в любом случае использовать макрос вместо typedef?). Кроме того, вопрос был не о Objective-C.
Тамас Селеи
@ TamásSzelei У меня была привычка делиться файлами заголовков на нескольких языках, и обычно все заголовки взяты из Objective-C. Мой новый код теперь не пахнет слишком сильно, но двойное подчеркивание все еще время от времени используется, чтобы придерживаться привычек ObjC.
Maxthon Chan