2009-10-20 4 views
216

Есть ли такая вещь? Это первый раз, когда я столкнулся с практической необходимостью, но я не вижу ни одного из перечисленных in Stroustrup. Я намерен написать:Логический оператор XOR в C++?

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

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

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

+54

На самом деле, Джим, это не единственная разница между & и &&, например ... 1 && 2 - True. но 1 & 2 => 0. Из-за этого я считаю, что «короткое замыкание» - это просто свойство, которое у них есть. Логическая - более важная функция ... –

+5

Не говоря уже о том, что 2 && 3 == true, но 2 & 3 == 2. –

+1

David Thomley: Ну да, но 2 ==> true, так что все в порядке ... Помните, что на самом деле не существует никаких логических элементов ... –

ответ

414

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

+0

Если оба являются ложными, разве XOR не должен возвращать false ??? В этом случае значение! = Вернет true. –

+1

Дэвид: F! = F ==> F. –

+5

@ Дэвид Брунелле: А? Что заставляет вас думать, что 'false! = False' будет оцениваться как' true'? – AnT

14

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

+0

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

+0

Ах, я никогда не понимал, что выбор и vs && был о коротком замыкании eval - я думал, что это просто соображение прозрачности кода. Благодарю. – RAC

+3

@RAC: Собственно, это важно знать. Вот почему такие вещи, как 'if (x! = NULL && x-> IsValid())' работают правильно. С помощью '&' он попытается оценить 'x-> IsValid()', даже если указатель 'x' равен' null'. –

29

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

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

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

a ? !b : b 

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

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

++x > 1 && x < 5 

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

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

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

+1

Вам не хватает другой важной информации о '||' и '&&': C) они оценивают операнды в булевом контексте. То есть '1 && 2' истинно, в отличие от' 1 & 2', которое равно нулю.Аналогично, оператор '^^' может быть полезен для предоставления этой дополнительной функции, оценки операндов в булевом контексте. Например. '1 ^^ 2' является ложным (в отличие от' 1^2'). –

+1

@Craig McQueen: Я не пропустил его. Второй абзац моего сообщения упоминает об этом. На мой взгляд, обработка операндов в виде булевых значений не является критическим признаком логических операторов, в некотором смысле, что они не будут введены только по этой причине. Основная причина, по которой они были введены, - это короткозамкнутая оценка и требуемая для этого последовательность. – AnT

+0

В настоящее время ваше предложение будет работать только с макросом? Хотя верно, что порядок параметров, которые должны оцениваться в функции, зависит от компилятора, разве в настоящее время он редко отличается от слева направо? Кроме того, в комментариях стоит отметить, что если реализация выглядит так: #define XOR (ll, rr) {ll? ! rr: rr} ', тогда вызов типа' int x = 2; XOR (++ x> 1, x <5); 'даст неправильный результат. Вызов должен иметь дополнительные скобки, например, в 'int x = 2; XOR ((++ x> 1), (x <5)); ', чтобы дать правильный ожидаемый результат. – user2019840

1

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

if (first xor second)... 

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

+1

Я догадываюсь, что они скрыты где-то #defines. Я уверен, что «и» и «xor» не являются ключевыми словами в ansi C ... (по крайней мере, не C79) –

+5

'xor' идентичен'^'... –

+1

@Brian Postow: я не знать, что такое C79, но в C++ 98 'and' и' xor' являются стандартными библиотечными макросами. Тей не из «где-то», они от . Эти макросы также находятся в C99 (не уверены в C89/90). – AnT

185

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

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

Я не понимаю, почему A и B сбрасываются! –

+15

Главным образом, чтобы преобразовать их в логические. '!!' будет работать, просто спросите хорошо, но поскольку они должны быть разными, их отрицание не наносит вреда. – LiraNuna

+3

Вопрос в том, что компиляторы смогут правильно оптимизировать это. – einpoklum

8

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

Обратите внимание, что я должен был добавить! 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 
23

Существует еще один способ сделать 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; 
} 
-2
#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

Это нарушает правило двойного подчеркивания. –

+0

@ TamásSzelei Не обязательно, поскольку компилятор не видит этого, поскольку он предварительно обработан, а в объективе-C двойные подчеркивания довольно распространены. –

+0

Хороший вопрос о препроцессоре, хотя для меня это все равно код запах таким образом (зачем использовать макрос, а не typedef?). Кроме того, вопрос был не в Objective-C. –

4

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

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

Вот как я думаю, что вы пишете 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); 
2

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

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

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

|-----|-----|-----------| 
| A | B | A XOR B | 
|-----|-----|-----------| 
| T | T | False | 
|-----|-----|-----------| 
| T | F | True | 
|-----|-----|-----------| 
| F | T | True | 
|-----|-----|-----------| 
| F | F | False | 
|-----|-----|-----------| 
Смежные вопросы