web-dev-qa-db-fra.com

Pourquoi c ++ n'a-t-il pas && = ou || = pour les booléens?

Y a-t-il une "très mauvaise chose" qui peut se produire && = et || = ont été utilisés comme sucre syntaxique pour bool foo = foo && bar et bool foo = foo || bar?

111
Kache

Un bool peut uniquement être true ou false en C++. En tant que tel, utiliser &= Et |= Est relativement sûr (même si je n'aime pas particulièrement la notation). Certes, ils effectueront des opérations binaires plutôt que des opérations logiques (et donc ils ne court-circuiteront pas), mais ces opérations binaires suivent un mappage bien défini, qui est effectivement équivalent aux opérations logiques, tant que les deux opérandes sont de type bool.1

Contrairement à ce que d'autres personnes ont dit ici, un bool en C++ ne doit jamais avoir une valeur différente telle que 2. Lorsque vous affectez cette valeur à un bool, elle sera convertie en true selon la norme.

La seule façon d'obtenir une valeur non valide dans un bool est d'utiliser reinterpret_cast Sur les pointeurs:

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

Mais puisque ce code entraîne de toute façon un comportement indéfini, nous pouvons ignorer en toute sécurité ce problème potentiel dans la conformité du code C++.


1 Certes, il s'agit d'une mise en garde assez importante, comme l'illustre le commentaire d'Angew:

bool b = true;
b &= 2; // yields `false`.

La raison en est que b & 2 Effectue une promotion d'entier telle que l'expression est alors équivalente à static_cast<int>(b) & 2, ce qui donne 0, Qui est ensuite reconverti en bool. Il est donc vrai que l'existence d'un operator &&= Améliorerait la sécurité des types.

68
Konrad Rudolph

&& et & ont une sémantique différente: && n'évaluera pas le deuxième opérande si le premier opérande est false. c'est-à-dire quelque chose comme

flag = (ptr != NULL) && (ptr->member > 3);

est sûr, mais

flag = (ptr != NULL) & (ptr->member > 3);

ne l'est pas, bien que les deux opérandes soient de type bool.

Il en va de même pour &= et |=:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

se comportera différemment de:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
43
Niki

Réponse courte

Tous les opérateurs +=, -=, *=, /=, &=, |= ... sont arithmétiques et fournir la même attente:

x &= foo()  // We expect foo() be called whatever the value of x

Cependant, les opérateurs &&= Et ||= Seraient logiques, et ces opérateurs pourraient être sujets à erreur car de nombreux développeurs s'attendraient à ce que foo() soit toujours appelé dans x &&= foo().

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • Avons-nous vraiment besoin de rendre le C/C++ encore plus complexe pour obtenir un raccourci pour x = x && foo()?

  • Voulons-nous vraiment obscurcir davantage l'instruction cryptique x = x && foo()?
    Ou voulons-nous écrire du code significatif comme if (x) x = foo();?


Longue réponse

Exemple pour &&=

Si l'opérateur &&= Était disponible, alors ce code:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

est équivalent à:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

Ce premier code est sujet aux erreurs car de nombreux développeurs penseraient que f2() est toujours appelée quelle que soit la valeur renvoyée par f1(). C'est comme écrire bool ok = f1() && f2();f2() est appelé uniquement lorsque f1() renvoie true.

  • Si le développeur souhaite que f2() soit appelé uniquement lorsque f1() renvoie true, le deuxième code ci-dessus est donc moins sujet aux erreurs.
  • Sinon (le développeur veut que f2() soit toujours appelée), &= Est suffisant:

Exemple pour &=

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

De plus, il est plus facile pour le compilateur d'optimiser ce code ci-dessus que celui ci-dessous:

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

Comparez && Et &

On peut se demander si les opérateurs && Et & Donnent le même résultat lorsqu'ils sont appliqués sur des valeurs bool?

Vérifions à l'aide du code C++ suivant:

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

Sortie:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

Conclusion

Par conséquent OUI nous pouvons remplacer && Par & Pour les valeurs de bool ;-)
Il vaut donc mieux utiliser &= Au lieu de &&=.
Nous pouvons considérer &&= Comme inutile pour les booléens.

Pareil pour ||=

l'opérateur |= est également moins sujet aux erreurs que ||=

Si un développeur souhaite que f2() soit appelé uniquement lorsque f1() renvoie false, au lieu de:

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

Je conseille l'alternative la plus compréhensible suivante:

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

ou si vous préférez tout en une ligne style:

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
24
olibre