Operador XOR lógico em C ++?

292

Existe uma coisa dessas? É a primeira vez que encontrei uma necessidade prática, mas não vejo uma listada no Stroustrup . Eu pretendo escrever:

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

Mas não há ^^operador. Posso usar o bit a bit ^aqui e obter a resposta certa (independentemente da representação da máquina como verdadeira e falsa)? Eu nunca misturo &e &&, ou |e ||, por isso hesito em fazer isso com ^e ^^.

Eu ficaria mais confortável escrevendo minha própria bool XOR(bool,bool)função.

RAC
fonte
57
Na verdade, Jim, essa não é a única diferença entre & e &&, por exemplo ... 1 && 2 é Verdadeiro. mas 1 e 2 => 0. Por causa disso, acho que "curto-circuito" é apenas uma propriedade que eles possuem. Lógico é a característica mais importante ...
Brian Postow
6
Sem mencionar que 2 && 3 == é verdade, mas 2 & 3 == 2. #
482 David Thornley
1
David Thomley: Bem, sim, mas 2 ==> true, de modo que é ok ... Lembre-se, não há realmente nenhum booleans ...
Brian Postow
13
@BrianPostow: Na verdade, em C ++, existem.
Adrian Willenbücher
7
Como postado abaixo, aqui está a resposta de Dennis Ritchie sobre o porquê de ele não existir: c-faq.com/misc/xor.dmr.html
Tobia

Respostas:

536

O !=operador serve a esse propósito para boolvalores.

Greg Hewgill
fonte
7
! Mas falsa = false => false
Jonas
13
Observe que isso funciona apenas para booleanos. E ^ funcionaria perfeitamente bem lá. 2! = 1 => 1, que não é o que você deseja! como o LiraNuna diz, colocando um! na frente de ambos os lados resolve esse problema. mas, novamente, então você pode usar bitwise ^ ...
Brian Postow
8
Certo, tomei o cuidado de mencionar "para boolvalores" porque não faz necessariamente o que você pode querer para não-booleanos. E como esse é o C ++, existe um booltipo real em vez de ser necessário intpara esse fim.
Greg Hewgill 20/10/2009
29
Se você quiser fazê-lo para o tipo de aapenas escrever!(a) != !(a)
Chris Lutz
6
@ ChrisLutz: sim, mas cuidado com os operadores sobrecarregados.
Rwong 11/03/2015
246

Para uma verdadeira operação lógica do XOR, isso funcionará:

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

Observe que !existem para converter os valores em booleanos e negá-los, para que dois inteiros positivos desiguais (cada um true) sejam avaliados como false.

LiraNuna
fonte
6
Não entendo por que A e B são negados!
Martin Hansen
26
Principalmente para convertê-los em booleanos. !!funcionaria apenas pedir bem, mas como eles precisam ser diferentes, negá-los não faz mal.
LiraNuna
4
A questão é: os compiladores podem otimizar isso adequadamente?
Einpoklum
6
Não saber a importância de normalizar os bools me custou 2 dias.
Makan Tayebi
9
@LiraNuna, "por que A e B são negados!" / "Principalmente para convertê-los em booleanos." - Acho que vale a pena mencionar na resposta.
cp.engr
42

A implementação manual correta do XOR lógico depende de quão próximo você deseja imitar o comportamento geral de outros operadores lógicos ( ||e &&) com o seu XOR. Há duas coisas importantes sobre esses operadores: 1) garantem a avaliação de curto-circuito, 2) introduzem um ponto de sequência, 3) avaliam seus operandos apenas uma vez.

A avaliação do XOR, como você entende, não pode ter um curto-circuito, pois o resultado sempre depende dos dois operandos. Então, 1 está fora de questão. Mas e quanto a 2? Se você não se importa com 2, o booloperador com valores normalizados (ou seja ) !=realiza o trabalho de XOR em termos do resultado. E os operandos podem ser facilmente normalizados com unário !, se necessário. Assim, !A != !Bimplementa o XOR apropriado a esse respeito.

Mas se você se importa com o ponto extra de sequência, !=nem a bit ^a bit é a maneira correta de implementar o XOR. Uma maneira possível de executar o XOR (a, b) corretamente pode ser a seguinte

a ? !b : b

Este é realmente o mais perto que você pode começar a fazer um XOR caseiro "similar" à ||e &&. Obviamente, isso só funcionará se você implementar seu XOR como uma macro. Uma função não funciona, pois o seqüenciamento não se aplica aos argumentos da função.

Alguém pode dizer, porém, que a única razão de ter um ponto de sequência em cada um &&e ||é apoiar a avaliação em curto-circuito e, portanto, o XOR não precisa de um. Isso faz sentido, na verdade. No entanto, vale a pena considerar ter um XOR com um ponto de sequência no meio. Por exemplo, a seguinte expressão

++x > 1 && x < 5

definiu comportamento e resultado específico em C / C ++ (com relação ao seqüenciamento, pelo menos). Portanto, pode-se esperar razoavelmente o mesmo do XOR lógico definido pelo usuário , como em

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

enquanto um !=XOR baseado em não tem essa propriedade.

Formiga
fonte
1
Você está perdendo a outra coisa importante ||e &&: C) eles avaliam os operandos em um contexto booleano. Ou seja, 1 && 2é verdade, ao contrário de 1 & 2zero. Da mesma forma, um ^^operador pode ser útil para fornecer esse recurso extra, para avaliar os operandos em um contexto booleano. Por exemplo, 1 ^^ 2é falso (diferente 1 ^ 2).
Craig McQueen
2
@ Craig McQueen: Não estou perdendo. O segundo parágrafo do meu post menciona isso. Na minha opinião, tratar operandos como valores booleanos não é uma característica crítica dos operadores lógicos, no sentido de que eles não seriam introduzidos apenas por esse motivo. A principal razão pela qual eles foram introduzidos é a avaliação em curto-circuito e o ponto de sequência necessário para isso.
AnT
Atualmente, sua sugestão ainda funcionaria apenas com uma macro? Embora seja verdade que a ordem dos parâmetros a serem avaliados em uma função depende do compilador, atualmente não é raro diferir da esquerda para a direita? Além disso, vale a pena observar aqui nos comentários que, se uma implementação for semelhante #define XOR(ll,rr) { ll ? !rr : rr }, uma chamada como int x = 2; XOR(++x > 1, x < 5);dará o resultado errado. A chamada teria que ter parênteses extras, como em int x = 2; XOR( (++x > 1), (x < 5) );, para fornecer o resultado esperado correto.
RAs 02/01
1
Como o XOR não pode causar um curto-circuito, não há necessidade de um ponto de sequência. XOR é mais parecido com + nesse sentido. A menos que você também queira argumentar que (++ x) + x é igual a 2x + 1, o ponto de sequência não é razoável.
precisa
1
@hkBst: Eu acho que isso é totalmente coberto na segunda parte da minha resposta.
AnT
25

Há outra maneira de fazer o XOR:

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

O que obviamente pode ser demonstrado para funcionar através de:

#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;
}
Bertie Wheen
fonte
7
Esta abordagem pode gerar algum código bastante lento - 3-5x mais lento do que (a!) = Em ambos clang e gcc usando libstdc ++: (b!) Quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q
xaxxon
18

O operador XOR não pode ter um curto-circuito; ou seja, você não pode prever o resultado de uma expressão XOR apenas avaliando seu operando esquerdo. Portanto, não há razão para fornecer uma ^^versão.

Mehrdad Afshari
fonte
25
-1 porque a principal diferença entre && e & não é apenas o curto-circuito. 1 && 2 é True, mas 1 & 2 é false. Curto-circuito é apenas um efeito colateral útil.
22611 Brian Postow
6
A resposta não está falando sobre &&e &em tudo. Seu argumento é que não há razão para apresentar ^^. A propriedade que ^^consideraria qualquer valor não nulo como 1não é realmente útil, eu suspeito. Ou pelo menos não consigo ver nenhum uso.
Johannes Schaub - litb 20/10/09
6
Também C ++! = Outras línguas. Em C e C ++, como mostrado acima, o curto-circuito não é apenas algo bom de se ter, mas é fundamentalmente importante. :)
Johannes Schaub - litb 20/10/09
13
Então você deve ler a resposta de Dennis Ritchie sobre por que ela não existe: it.usyd.edu.au/~dasymond/mirror/c-faq/misc/xor.dmr.html
Johannes Schaub #
5
Aqui está um link trabalhando em resposta de Dennis Ritchie a respeito de porque ela não existe: c-faq.com/misc/xor.dmr.html
Tobia
13

Houve um bom código postado que resolveu o problema melhor do que! A! =! B

Observe que eu tive que adicionar o BOOL_DETAIL_OPEN / CLOSE para que ele funcionasse no 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
dados elegantes
fonte
6

Use um simples:

return ((op1 ? 1 : 0) ^ (op2 ? 1 : 0));
tiands
fonte
5

Aqui está como eu acho que você escreve uma comparação XOR em 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
}

Prova

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

A prova é que um estudo exaustivo de entradas e saídas mostra que, nas duas tabelas, para cada conjunto de entradas, o resultado é sempre o mesmo nas duas tabelas.

Portanto, a pergunta original é como escrever:

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

A resposta seria

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

Ou, se quiser, escreva

return !(A==5) == (B==5);
Indinfer
fonte
! a! =! b parece ser mais agradável porque converte seus argumentos em bool para você.
xaxxon
3

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

A primeira parte é A OR B, que é o OR Inclusivo; a segunda parte é, NÃO A e B. Juntos, você obtém A ou B, mas não ambos, A e B.

Isso fornecerá o XOR comprovado na tabela verdade abaixo.

|-----|-----|-----------|
|  A  |  B  |  A XOR B  |
|-----|-----|-----------|
|  T  |  T  |   False   |
|-----|-----|-----------|
|  T  |  F  |   True    |
|-----|-----|-----------|
|  F  |  T  |   True    |
|-----|-----|-----------|
|  F  |  F  |   False   |
|-----|-----|-----------|
Comer no Joes
fonte
Eu não estou cavando o desempenho desta abordagem: quick-bench.com/PgNgGN8ATrKt7el1dAaJj7QtuF4
xaxxon
Não há necessidade de ficar na defensiva sobre isso.
Xaxxon
1
@xaxxon: Não vê o ponto nestes benchmarks !? No StringCreation você usou na criação de string, mas no segundo benchmark não. Se você colocar um código idêntico nos dois benchmarks e chamar XORs diferentes para cada benchmark (XOR e XOR2), obterá os mesmos resultados de benchmark. Então, o que você está tentando dizer?
SoLaR
1

Eu uso "xor" (parece que é uma palavra-chave; em Code :: Blocks, pelo menos, fica em negrito), assim como você pode usar "e" em vez de &&e "ou" em vez de ||.

if (first xor second)...

Sim, é bit a bit. Desculpe.

csiz
fonte
2
Eu estou supondo que esses ocultos # definem de algum lugar. Eu tenho certeza "e" e "xor" não são palavras-chave em ansi C ... (pelo menos não C79)
Brian Postow
1
@ Brian Postow: Eu não sei o que é C79, mas em C ++ 98 ande xorsão macros de biblioteca padrão. Eles não são de "algum lugar", eles são de <iso646.h>. Essas macros também estão no C99 (não tenho certeza sobre o C89 / 90).
AnT
5
@Brian Postow: ... xorsignifica bit a bit xor, embora andseja lógico e.
AnT
1
Eu digitei C89 como C79 ... e xor etc não estão na minha cópia da K&R. Eu acho que nunca usei o iso686.h, pelo menos não conscientemente ... e então, sim, eles são #define de algum lugar, você só sabe onde fica esse lugar B-)
Brian Postow
2
Eles certamente estão em C99 usando esse cabeçalho. No C ++, eles são integrados à linguagem como "tokens alternativos" e você pode struct A { compl A() { } };definir um destruidor, por exemplo.
Johannes Schaub - litb 21/10/09
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);
}

Funciona como definido. Os condicionais são para detectar se você está usando Objective-C , que está pedindo BOOL em vez de bool (o comprimento é diferente!)

Maxthon Chan
fonte
3
Isso viola a regra de sublinhado duplo.
Tamás Szelei
@ TamásSzelei Não necessariamente porque o compilador não vê isso como é pré-processado, e no Objective-C o mundo com dois sublinhados é bastante comum.
Maxthon Chan
Bom argumento sobre o pré-processador, embora para mim ainda seja o cheiro do código dessa maneira (por que usar uma macro em vez de um typedef, afinal?). Além disso, a pergunta não era sobre o Objective-C.
Tamás Szelei
@ TamásSzelei Bem, eu costumava compartilhar arquivos de cabeçalho em vários idiomas, e geralmente todos os cabeçalhos vêm do Objective-C. Meu novo código não cheira muito agora, mas o sublinhado duplo ainda é usado de tempos em tempos para aderir aos hábitos ObjC.
Maxthon Chan