Question

Y a-t-il une raison de ne pas utiliser les opérateurs au niveau du bit &, | et ^ pour les valeurs « bool » en C++ ?

Je rencontre parfois des situations où je veux qu'exactement une des deux conditions soit vraie (XOR), donc je lance simplement l'opérateur ^ dans une expression conditionnelle.Je souhaite aussi parfois que toutes les parties d'une condition soient évaluées, que le résultat soit vrai ou non (plutôt que de court-circuiter), j'utilise donc & et |.J'ai aussi parfois besoin d'accumuler des valeurs booléennes, et &= et |= peuvent être très utiles.

J'ai eu quelques sourcils haussés en faisant cela, mais le code est toujours significatif et plus propre qu'il ne le serait autrement.Y a-t-il une raison de NE PAS les utiliser pour les booléens ?Existe-t-il des compilateurs modernes qui donnent de mauvais résultats pour cela ?

Était-ce utile?

La solution

|| et && sont des opérateurs booléens et ceux intégrés sont garantis de renvoyer soit true ou false.Rien d'autre.

|, & et ^ sont des opérateurs au niveau du bit.Lorsque le domaine des nombres sur lequel vous opérez est juste 1 et 0, alors ils sont exactement les mêmes, mais dans les cas où vos booléens ne sont pas strictement 1 et 0 – comme c'est le cas avec le langage C – vous pouvez vous retrouver avec un certain comportement. tu ne voulais pas.Par exemple:

BOOL two = 2;
BOOL one = 1;
BOOL and = two & one;   //and = 0
BOOL cand = two && one; //cand = 1

En C++, cependant, le bool type est garanti comme étant uniquement soit un true ou un false (qui se convertissent implicitement en respectivement 1 et 0), cette position est donc moins inquiétante, mais le fait que les gens ne soient pas habitués à voir de telles choses dans le code constitue un bon argument pour ne pas le faire.Dis le b = b && x et en finir avec ça.

Autres conseils

Deux raisons principales.Bref, réfléchissez bien ;il pourrait y avoir une bonne raison à cela, mais si vos commentaires sont TRÈS explicites, car ils peuvent être fragiles et, comme vous le dites vous-même, les gens ne sont généralement pas habitués à voir du code comme celui-ci.

Xor au niveau du bit != Xor logique (sauf pour 0 et 1)

Premièrement, si vous opérez sur des valeurs autres que false et true (ou 0 et 1, sous forme d'entiers), le ^ L'opérateur peut introduire un comportement non équivalent à un xor logique.Par exemple:

int one = 1;
int two = 2;

// bitwise xor
if (one ^ two)
{
  // executes because expression = 3 and any non-zero integer evaluates to true
}

// logical xor; more correctly would be coded as
//   if (bool(one) != bool(two))
// but spelled out to be explicit in the context of the problem
if ((one && !two) || (!one && two))
{
  // does not execute b/c expression = ((true && false) || (false && true))
  // which evaluates to false
}

Nous remercions l'utilisateur @Patrick d'avoir exprimé cela en premier.

Ordre des opérations

Deuxième, |, &, et ^, en tant qu'opérateurs au niveau du bit, ne court-circuitent pas.De plus, plusieurs opérateurs au niveau du bit enchaînés dans une seule instruction – même avec des parenthèses explicites – peuvent être réorganisés en optimisant les compilateurs, car les 3 opérations sont normalement commutatives.Ceci est important si l’ordre des opérations compte.

Autrement dit

bool result = true;
result = result && a() && b();
// will not call a() if result false, will not call b() if result or a() false

ne donnera pas toujours le même résultat (ou état final) que

bool result = true;
result &= (a() & b());
// a() and b() both will be called, but not necessarily in that order in an
// optimizing compiler

Ceci est particulièrement important car vous ne pouvez pas contrôler les méthodes a() et b(), ou quelqu'un d'autre peut venir les modifier plus tard sans comprendre la dépendance et provoquer un bug désagréable (et souvent uniquement lors de la version de version).

Je pense

a != b

c'est ce que tu veux

Les sourcils levés devraient vous en dire assez pour arrêter de le faire.Vous n'écrivez pas le code pour le compilateur, vous l'écrivez d'abord pour vos collègues programmeurs, puis pour le compilateur.Même si les compilateurs fonctionnent, surprendre les autres n'est pas ce que vous voulez - les opérateurs au niveau du bit sont destinés aux opérations sur les bits et non aux booléens.
Je suppose que tu manges aussi des pommes avec une fourchette ?Ça marche mais ça surprend les gens donc mieux vaut ne pas le faire.

Inconvénients des opérateurs de niveau bit.

Tu demandes:

"Y a-t-il une raison de ne pas utiliser les opérateurs au niveau du bit ? &, |, et ^ pour les valeurs "booléennes" en C++ ?»

Oui le Opérateurs logiques, c'est-à-dire les opérateurs booléens de haut niveau intégrés !, && et ||, offrent les avantages suivants :

  • Garanti conversion d'arguments à bool, c'est à dire.à 0 et 1 valeur ordinale.

  • Garanti évaluation des courts-circuits où l’évaluation de l’expression s’arrête dès que le résultat final est connu.
    Cela peut être interprété comme une logique d'arbre de valeurs, avec Vrai, FAUX et Indéterminé.

  • Équivalents textuels lisibles not, and et or, même si je ne les utilise pas moi-même.
    Comme le note le lecteur Antimony dans un commentaire, les opérateurs de niveau bit ont également des jetons alternatifs, à savoir bitand, bitor, xor et compl, mais à mon avis, ceux-ci sont moins lisibles que and, or et not.

En termes simples, chacun de ces avantages des opérateurs de haut niveau est un inconvénient des opérateurs de niveau bit.

En particulier, étant donné que les opérateurs au niveau du bit n'ont pas de conversion d'argument en 0/1, vous obtenez par ex. 1 & 20, alors que 1 && 2true.Aussi ^, exclusif au niveau du bit ou, peut mal se comporter de cette façon.Considérées comme les valeurs booléennes 1 et 2 sont identiques, à savoir true, mais considérés comme des modèles de bits, ils sont différents.


Comment exprimer la logique soit/ou en C++.

Vous fournissez ensuite un peu de contexte pour la question,

"Je rencontre parfois des situations où je veux qu'exactement une des deux conditions soit vraie (XOR), alors je lance simplement l'opérateur ^ dans une expression conditionnelle."

Eh bien, les opérateurs au niveau du bit ont priorité supérieure que les opérateurs logiques.Cela signifie notamment que dans une expression mixte telle que

a && b ^ c

vous obtenez un résultat peut-être inattendu a && (b ^ c).

Au lieu de cela, écrivez simplement

(a && b) != c

exprimer de manière plus concise ce que vous voulez dire.

Pour l'argument multiple soit/ou il n'y a pas d'opérateur C++ qui fait le travail.Par exemple, si vous écrivez a ^ b ^ c que ça n’est pas une expression qui dit « soit a, b ou c est vrai".Au lieu de cela, il est écrit : « Un nombre impair de a, b et c sont vrais", ce qui peut être l'un d'entre eux ou les 3…

Pour exprimer le général soit/ou quand a, b et c sont du type bool, Ecrivez

(a + b + c) == 1

ou, avec des non-bool arguments, convertissez-les en bool:

(!!a + !!b + !!c) == 1


En utilisant &= pour accumuler les résultats booléens.

Vous développez davantage,

«J'ai aussi parfois besoin d'accumuler des valeurs booléennes, et &= et |=? peut être très utile.

Eh bien, cela correspond à vérifier si respectivement tous ou n'importe lequel la condition est satisfaite, et la loi de Morgan vous indique comment passer de l'un à l'autre.C'est à dire.vous n’en avez besoin que d’un seul.Vous pourriez en principe utiliser *= comme un &&=-opérateur (comme l'a découvert le bon vieux George Boole, le ET logique peut très facilement être exprimé sous forme de multiplication), mais je pense que cela rendrait perplexe et induirait peut-être en erreur les responsables du code.

Considérez également :

struct Bool
{
    bool    value;

    void operator&=( bool const v ) { value = value && v; }
    operator bool() const { return value; }
};

#include <iostream>

int main()
{
    using namespace std;

    Bool a  = {true};
    a &= true || false;
    a &= 1234;
    cout << boolalpha << a << endl;

    bool b = {true};
    b &= true || false;
    b &= 1234;
    cout << boolalpha << b << endl;
}

Sortie avec Visual C++ 11.0 et g++ 4.7.1 :

true
false

La raison de la différence dans les résultats est que le niveau de bit &= ne fournit pas de conversion en bool de son argument du côté droit.

Alors, lequel de ces résultats désirez-vous pour votre utilisation de &=?

Si le premier, true, puis mieux définir un opérateur (par ex.comme ci-dessus) ou une fonction nommée, ou utilisez une conversion explicite de l'expression de droite, ou écrivez la mise à jour dans son intégralité.

Contrairement à la réponse de Patrick, le C++ n'a pas ^^ opérateur pour effectuer un court-circuit exclusif ou.Si vous y réfléchissez une seconde, avoir un ^^ l'opérateur n'aurait aucun sens de toute façon :avec ou exclusif, le résultat dépend toujours des deux opérandes.Cependant, l'avertissement de Patrick concernant le non-bool Les types "booléens" sont également valables lors de la comparaison 1 & 2 à 1 && 2.Un exemple classique est celui de Windows GetMessage() fonction, qui renvoie un tri-état BOOL:différent de zéro, 0, ou -1.

En utilisant & au lieu de && et | au lieu de || n'est pas une faute de frappe rare, donc si vous le faites délibérément, elle mérite un commentaire expliquant pourquoi.

Patrick a fait valoir de bons arguments et je ne vais pas les répéter.Cependant, puis-je suggérer de réduire les instructions « if » en anglais lisible autant que possible en utilisant des variables booléennes bien nommées. Par exemple, cela utilise des opérateurs booléens, mais vous pouvez également utiliser au niveau du bit et nommer les booléens de manière appropriée :

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here
bool onlyBIsTrue = (b && !a); // and not need this second line
if (onlyAIsTrue || onlyBIsTrue)
{
 .. stuff ..
}

Vous pourriez penser que l’utilisation d’un booléen semble inutile, mais cela aide à deux choses principales :

  • Votre code est plus facile à comprendre car le booléen intermédiaire de la condition « if » rend l'intention de la condition plus explicite.
  • Si vous utilisez du code non standard ou inattendu, tel que des opérateurs au niveau du bit sur des valeurs booléennes, les utilisateurs peuvent comprendre beaucoup plus facilement pourquoi vous avez fait cela.

MODIFIER:Vous n'avez pas explicitement dit que vous vouliez les conditions pour les instructions « if » (bien que cela semble le plus probable), c'était mon hypothèse.Mais ma suggestion d’une valeur booléenne intermédiaire est toujours valable.

IIRC, de nombreux compilateurs C++ vous avertiront lorsqu'ils tenteront de convertir le résultat d'une opération au niveau du bit en booléen.Vous devrez utiliser un transtypage pour rendre le compilateur heureux.

Utiliser une opération au niveau du bit dans une expression if servirait la même critique, mais peut-être pas de la part du compilateur.Toute valeur non nulle est considérée comme vraie, donc quelque chose comme « si (7 & 3) » sera vrai.Ce comportement peut être acceptable en Perl, mais C/C++ sont des langages très explicites.Je pense que le sourcil de Spock est une preuve de diligence raisonnable.:) J'ajouterais "== 0" ou "!= 0" pour indiquer parfaitement quel était votre objectif.

Quoi qu’il en soit, cela ressemble à une préférence personnelle.J'exécuterais le code via lint ou un outil similaire et verrais s'il pense également que c'est une stratégie imprudente.Personnellement, cela ressemble à une erreur de codage.

L'utilisation d'opérations au niveau du bit pour bool permet d'économiser la logique de prédiction de branchement inutile par le processeur, résultant d'une instruction « cmp » introduite par des opérations logiques.

Remplacer le logique par des opérations au niveau du bit (où tous les opérandes sont des booléens) génère un code plus efficace offrant le même résultat.Idéalement, l'efficacité devrait compenser tous les avantages de court-circuit qui peuvent être exploités lors de la commande à l'aide d'opérations logiques.

Cela peut rendre le code un peu illisible, même si le programmeur doit le commenter en expliquant les raisons pour lesquelles il l'a fait.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top