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


292

Чи є таке? Це перший раз, коли я зіткнувся з практичною потребою в цьому, але я не бачу жодної, переліченої в Stroustrup . Я маю намір написати:

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

Але ^^оператора немає . Чи можу я тут скористатися побітним способом ^і отримати правильну відповідь (незалежно від машинного подання істинного та хибного)? Я ніколи не змішую &і &&, або, |і ||, тому вагаюся робити це з ^і ^^.

Мені було б зручніше bool XOR(bool,bool)замість цього писати власну функцію.


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
Але false! = False => false
Йонас

13
Зауважте, що це працює лише для булів. І ^ добре би там працював. 2! = 1 => 1, що не є тим, що ти хочеш! як каже LiraNuna, ставлячи! Передньою стороною обидві сторони вирішують цю проблему. але знову ж таки, ви можете використовувати побітові ^ ...
Брайан Постув

8
Правильно, я обережно зазначив "для boolзначень", оскільки це не обов'язково робити те, що ви хочете для небулевих. А оскільки це C ++, існує реальний boolтип замість того, щоб використовувати intдля цієї мети.
Грег Хьюгілл

29
Якщо ви хочете зробити це для типу, aпросто напишіть!(a) != !(a)
Chris Lutz

6
@ChrisLutz: так, але остерігайтеся перевантажених операторів.
rwong

246

Для справжньої логічної операції XOR це спрацює:

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

Зауважте, що !є для перетворення значень в булеві та заперечення їх, щоб два нерівні додатні цілі числа (кожне a true) оцінювали false.


6
Я не розумію, чому A і B заперечуються!
Мартін Хансен

26
Головним чином, щоб перетворити їх на булеві. !!працювали б просто запитати добре, але оскільки вони мають бути різними, нехтування ними не приносить шкоди.
LiraNuna

4
Питання в тому, чи зможуть компілятори правильно це оптимізувати.
einpoklum

6
Не знаючи важливості нормалізації булінів, коштувало мені 2 дні.
Makan Tayebi

9
@LiraNuna, "чому A і B заперечуються!" / "Головним чином, щоб перетворити їх на булеві." - Я думаю, що це варто згадати у відповіді.
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 }, то виклик, як, наприклад int x = 2; XOR(++x > 1, x < 5);, дасть неправильний результат. Виклик повинен мати додаткові дужки, як, наприклад int x = 2; XOR( (++x > 1), (x < 5) );, для того, щоб дати правильний очікуваний результат.
RAs

1
Оскільки XOR не може мати коротке замикання, немає потреби в точці послідовності. У цьому відношенні XOR більше схожий на +. Якщо ви також не хочете стверджувати, що (++ x) + x дорівнює 2x + 1, точка послідовності не є розумною.
hkBst

1
@hkBst: Я думаю, що це повністю висвітлено у другій частині моєї відповіді.
АН

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
Йоханнес Шауб - ліб

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


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);

! a! =! b здається приємнішим, тому що він перетворює ваші аргументи на користь для вас.
xaxxon

3

(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   |
|-----|-----|-----------|

Я не копаю
xaxxon

Не потрібно в цьому захищатись.
xaxxon

1
@xaxxon: Не бачите сенсу в цих орієнтирах !? У StringCreation ви використовували для створення рядка, але у другому еталоні ви цього не зробили. Якщо ви розмістите однаковий код в обох орієнтирах і зателефонуєте різним XOR для кожного еталону (XOR та XOR2), ви отримаєте однакові результати порівняння. То що ви намагалися сказати?
SoLaR

1

Я використовую "xor" (здається, це ключове слово; у Code :: Blocks принаймні він стає жирним) так само, як ви можете використовувати "і" замість &&і "або" замість ||.

if (first xor second)...

Так, це побіжно. Вибачте.


2
Я здогадуюсь, що вони десь приховані #defines. Я впевнений, що "і" і "xor" - це не ключові слова в ansi C ... (принаймні, не C79)
Brian Postow

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 звідкись, ви просто
випадково

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ás Szelei

@ TamásSzelei Не обов'язково, оскільки компілятор не бачить, що він попередньо обробляється, і в світі Objective-C подвійні підкреслення є досить поширеними.
Макшон Чан

Хороший момент щодо препроцесора, хоча для мене це все ще пахне кодом таким чином (навіщо все-таки використовувати макрос, а не typedef?). Також питання не стосувалося Objective-C.
Tamás Szelei

@ TamásSzelei Добре, що я звик ділитися файлами заголовків на декількох мовах, і зазвичай всі заголовки походять від Objective-C. Мій новий код зараз не надто пахне, але подвійне підкреслення все ще час від часу використовується для дотримання звичок ObjC.
Макшон Чан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.