326 votes

Opérateur logique XOR en C++ ?

Est-ce que ça existe ? C'est la première fois que j'en rencontre le besoin pratique, mais je n'en vois pas la liste. à Stroustrup . J'ai l'intention d'écrire :

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

Mais il n'y a pas ^^ opérateur. Puis-je utiliser l'opérateur bitwise ^ ici et obtenir la bonne réponse (quelle que soit la représentation machine du vrai et du faux) ? Je ne mélange jamais & y && o | y || donc j'hésite à le faire avec ^ y ^^ .

Je serais plus à l'aise pour écrire mes propres bool XOR(bool,bool) à la place.

59 votes

En fait, Jim, ce n'est pas la seule différence entre & et && par exemple... 1 && 2 est Vrai. mais 1 & 2 => 0. Pour cette raison, je pense que le "court-circuitage" est juste une propriété qu'ils ont par hasard. La logique est la caractéristique la plus importante...

7 votes

Sans compter que 2 && 3 == vrai, mais 2 & 3 == 2.

1 votes

David Thomley : Eh bien, oui, mais 2 ==> vrai, donc c'est bon... Souviens-toi, il n'y a pas vraiment de booléens...

605voto

Greg Hewgill Points 356191

El != sert à cette fin pour bool valeurs.

1 votes

Si les deux sont faux, le XOR ne devrait-il pas renvoyer false ? ?? Dans ce cas, le != renvoie vrai.

10 votes

Mais false != false => false

15 votes

Notez que cela ne fonctionne que pour les booléens. Et ^ fonctionnerait parfaitement bien dans ce cas. 2 !=1 => 1 ce qui n'est pas ce que vous voulez ! comme le dit LiraNuna, mettre un ! devant les deux côtés résout ce problème. mais encore une fois, vous pouvez utiliser ^ en mode bit...

274voto

LiraNuna Points 21565

Pour une véritable opération logique XOR, cela fonctionnera :

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

Notez le ! sont là pour convertir les valeurs en booléens et les nier, de sorte que deux entiers positifs inégaux (chacun a true ) serait évalué à false .

7 votes

Je ne comprends pas pourquoi A et B sont niés avec !

30 votes

Principalement pour les convertir en booléens. !! fonctionneraient tout aussi bien, mais comme ils doivent être différents, les nier ne fait pas de mal.

6 votes

La question est de savoir si les compilateurs sont capables de l'optimiser correctement.

44voto

AndreyT Points 139512

Manuel approprié logique La mise en œuvre de XOR dépend du degré d'imitation du comportement général des autres opérateurs logiques ( || y && ) avec votre XOR. Il y a deux choses importantes à propos de ces opérateurs : 1) ils garantissent une évaluation en court-circuit, 2) ils introduisent un point de séquence, 3) ils évaluent leurs opérandes une seule fois.

L'évaluation XOR, comme vous le comprenez, ne peut pas être court-circuitée puisque le résultat dépend toujours des deux opérandes. Le 1 est donc hors de question. Mais qu'en est-il de 2 ? Si vous ne vous préoccupez pas de 2, alors avec une évaluation normalisée (c.-à-d., une évaluation XOR), il est possible d'obtenir un résultat qui dépend des deux opérandes. bool ) opérateur de valeurs != fait le travail de XOR en termes de résultat. Et les opérandes peuvent être facilement normalisés avec unary ! si nécessaire. Ainsi, !A != !B implémente le XOR approprié à cet égard.

Mais si vous vous souciez du point de séquence supplémentaire, ni l'un ni l'autre != ni par bit ^ est la façon correcte d'implémenter XOR. Une façon possible de faire XOR(a, b) correctement pourrait ressembler à ceci

a ? !b : b

C'est en fait ce qui se rapproche le plus de la fabrication d'un XOR fait maison "semblable" à || y && . Cela ne fonctionnera, bien sûr, que si vous implémentez votre XOR comme une macro. Une fonction ne fera pas l'affaire, puisque le séquencement ne s'appliquera pas aux arguments de la fonction.

Quelqu'un pourrait dire, cependant, que la seule raison d'avoir un point de séquence à chaque fois && y || est de supporter l'évaluation court-circuitée, et donc XOR n'en a pas besoin. Cela a du sens, en fait. Pourtant, il est intéressant d'envisager d'avoir un XOR avec un point de séquence au milieu. Par exemple, l'expression suivante

++x > 1 && x < 5

a un comportement défini et un résultat spécifique en C/C++ (en ce qui concerne le séquençage au moins). On peut donc raisonnablement s'attendre à ce que la même chose se produise avec les fonctions logique XOR, comme dans

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

tandis qu'un != -Le XOR basé sur l'inverse n'a pas cette propriété.

1 votes

Tu manques l'autre chose importante à propos || y && : C) ils évaluent les opérandes dans un contexte booléen. C'est-à-dire, 1 && 2 est vrai, contrairement à 1 & 2 qui est égal à zéro. De même, un ^^ pourrait être utile pour fournir cette fonctionnalité supplémentaire, qui consiste à évaluer les opérandes dans un contexte booléen. Par exemple 1 ^^ 2 est fausse (contrairement à 1 ^ 2 ).

2 votes

@Craig McQueen : Je ne l'ai pas manqué. Le deuxième paragraphe de mon post le mentionne. À mon avis, traiter les opérandes comme des valeurs booléennes n'est pas une caractéristique critique des opérateurs logiques, dans un sens où ils n'auraient pas été introduits pour cette seule raison. La raison principale pour laquelle ils ont été introduits est l'évaluation en court-circuit et le point de séquence requis pour cela.

0 votes

De nos jours, votre suggestion ne fonctionnerait-elle toujours qu'avec une macro ? Même s'il est vrai que l'ordre des paramètres à évaluer dans une fonction dépend du compilateur, n'est-il pas actuellement rare de différer de gauche à droite ? De plus, il pourrait être utile de noter ici dans les commentaires que si une implémentation ressemble à #define XOR(ll,rr) { ll ? !rr : rr } puis un appel comme int x = 2; XOR(++x > 1, x < 5); donnera un résultat erroné. L'appel devrait avoir des parenthèses supplémentaires, comme dans int x = 2; XOR( (++x > 1), (x < 5) ); afin de donner le bon résultat attendu.

27voto

Bertie Wheen Points 529

Il y a une autre façon de faire le XOR :

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

Ce qui peut évidemment être démontré pour fonctionner via :

#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 votes

Cette approche peut générer un code assez lent - 3-5x plus lent que (!a) != (!b) sur clang et gcc en utilisant libstdc++ : quick-bench.com/xtv2StFkR8PCkV4fOiqSgeG1T4Q

1 votes

@xaxxon votre benchmark n'est pas correct : une des deux fonctions benchmarkées crée une chaîne (sans rapport) dans sa boucle. En supprimant cela, le code est seulement 30% plus lent. quick-bench.com/q/umQRhhr0ZVS2o03fhCQAfN3HLak

0 votes

@gmargari a dû oublier d'enlever le repère auto-généré avec lequel il commence. Désolé.

18voto

Mehrdad Afshari Points 204872

L'opérateur XOR ne peut pas être court-circuité ; c'est-à-dire que vous ne pouvez pas prédire le résultat d'une expression XOR en évaluant simplement son opérande gauche. Par conséquent, il n'y a aucune raison de fournir une fonction de type ^^ version.

2 votes

Pour clarifier, cela signifie que pour vérifier le XOR, vous devez évaluer les deux parties du test. Il n'y a donc aucun avantage à avoir un symbole. C/C++ est autorisé à sauter les parties inutiles d'une comparaison si le résultat final est connu après le premier test.

1 votes

Ah, je n'avais jamais réalisé que le choix de & vs && concernait l'évaluation des courts-circuits - je pensais que c'était juste une considération de clarté de code. Merci.

4 votes

@RAC : En fait, c'est une chose importante à savoir. C'est pourquoi des choses comme if (x != NULL && x->IsValid()) fonctionnent correctement. Avec & il essaierait d'évaluer x->IsValid() même si le x Le pointeur est null .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X