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

126

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

Kache
la source
5
Voir cette autre question: stackoverflow.com/questions/2324549/... Celle-ci concerne Java, mais partageant la lignée C, les mêmes arguments s'appliquent principalement.
jamesdlin
Fondamentalement, c ++ ne l'a pas parce qu'il ne l'a pas mis - des langages comme Ruby l'ont. boo ...
Kache
2
Mais en Ruby, n'est-il pas à x ||= ypeu près équivalent à C ++ x = x ? x : y;pour aucun type? En d'autres termes, "mis à y si ce n'est déjà fait". C'est considérablement plus utile que C ou C ++ x ||= y, qui (à l'exception de la surcharge des opérateurs) ferait "définir x à (bool)ysauf s'il est déjà défini". Je ne suis pas impatient d'ajouter un autre opérateur pour cela, cela semble un peu faible. Écrivez juste if (!x) x = (bool)y. Mais alors, je n'utilise pas vraiment boolassez de variables pour vouloir des opérateurs supplémentaires qui ne sont vraiment utiles qu'avec ce type.
Steve Jessop
1
Je suis sûr que la principale raison pour laquelle C ++ ne les a pas &&=ou ||=est simplement que C ne les a pas. Je suis raisonnablement sûr que la raison pour laquelle C ne les a pas est que la fonctionnalité n'a pas été jugée suffisamment bénéfique.
Jonathan Leffler
2
De plus, étant ultra-pédantique, la notation bool foo = foo || bar;invoquerait un comportement non défini car elle foon'est pas initialisée avant l'évaluation de foo || bar. Bien sûr, cela est censé être quelque chose comme bool foo = …initialization…; …; foo = foo || bar;et la question est alors valide.
Jonathan Leffler

Réponses:

73

Un boolpeut être uniquement trueou falseen 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 sur les bits plutôt que des opérations logiques (et donc ils ne court-circuiteront pas) mais ces opérations sur les bits suivent un mappage bien défini, qui est effectivement équivalent aux opérations logiques, tant que les deux opérandes sont de typebool . 1

Contrairement à ce que d'autres ont dit ici, un boolen C ++ ne doit jamais avoir une valeur différente telle que 2. Lors de l'attribution de cette valeur à a bool, elle sera convertie trueen selon la norme.

La seule façon d'obtenir une valeur non valide dans a boolest d'utiliser des reinterpret_castpointeurs:

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

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


1 Certes, c'est 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 & 2effectue une promotion entière de telle sorte que l'expression soit alors équivalente à static_cast<int>(b) & 2, ce qui entraîne 0, qui est ensuite reconvertie en un fichier bool. Il est donc vrai que l'existence d'un système operator &&=améliorerait la sécurité des types.

Konrad Rudolph
la source
4
Mais le && et || Les opérateurs travailleront sur tout ce qui se convertit en booléen, pas seulement en booléen.
dan04
13
Ils ne font pas la même chose, même sur les booléens. ||et &&raccourci, c'est-à-dire que le deuxième argument n'est pas un opérande si le premier opérande est true(resp. falsepour &&). |, &, |=Et &=toujours évaluer les deux opérandes.
Niki
4
cela ne répond pas à la question de savoir pourquoi && = et || = ne sont pas des opérateurs C ++.
thang
9
Il n'est pas sûr de l'utiliser &=pour un côté gauche de type bool, car il est parfaitement possible que le côté droit soit de type autre que bool(comme islowerou une autre fonction C stdlib qui renvoie une valeur différente de zéro pour la valeur vraie). Si nous avions l'hypothèse &&=, cela forcerait probablement le membre de droite à se convertir bool, ce qui &=n'est pas le cas. En d'autres termes, bool b = true; b &= 2;aboutit à b == false.
Angew n'est plus fier de SO
2
@Antonio Foule difficile. 😝
Konrad Rudolph
44

&&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);

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

La même chose est vraie pour &=et |=:

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

se comportera différemment de:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
Niki
la source
35
raison de plus d'avoir && = à mon avis. = P
Kache
4
Ce n'est pas vraiment une réponse.
Catskul
27

Réponse courte

Tous les opérateurs +=, -=, *=, /=, &=, |=... sont l' arithmétique et offrent 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 aux erreurs car de nombreux développeurs s'attendent à foo()être toujours appelés 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 C / C ++ encore plus complexe pour obtenir un raccourci x = x && foo()?

  • Voulons-nous vraiment obscurcir davantage la déclaration 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 qu'il f2()est toujours appelé quelle que soit la f1()valeur renvoyée. C'est comme écrire bool ok = f1() && f2();f2()n'est appelé que lors du f1()retour true.

  • Si le développeur souhaite réellement f2()être appelé uniquement lors des f1()retours true, le deuxième code ci-dessus est donc moins sujet aux erreurs.
  • Sinon (le développeur veut f2()toujours être appelé), &=suffit:

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 du 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 boolvaleurs?

Vérifions en utilisant le 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);
}

Production:

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 &des boolvaleurs ;-)
Donc, mieux vaut 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 f2()être appelé uniquement lors du f1()retour 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 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 dans un style de ligne :

// 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();
olibre
la source
13
Et si je veux vraiment ce comportement? Que l'expression de la main droite n'est pas exécutée si l'expression de la main gauche est fausse. Il est ennuyeux d'écrire les variables deux fois, commesuccess = success && DoImportantStuff()
Niklas R
1
Mon conseil est d'écrire if(success) success = DoImportantStuff(). Si l'instruction success &&= DoImportantStuff()était autorisée, de nombreux développeurs penseraient qu'elle DoImportantStuff()est toujours appelée quelle que soit la valeur de success. J'espère que cela répond à ce que vous vous demandez ... J'ai également amélioré de nombreuses parties de ma réponse. Veuillez me dire si ma réponse est plus compréhensible maintenant? (à propos de votre commentaire) Cheers, See you ;-)
olibre
9
"Si la déclaration success &&= DoImportantStuff()était autorisée, de nombreux développeurs penseraient qu'elle DoImportantStuff()est toujours appelée quelle que soit la valeur du succès." Vous pouvez en dire if (success && DoImportantStuff())autant. Tant qu'ils se souviennent de la logique derrière la syntaxe if, ils ne devraient avoir aucun problème avec &&=.
pilkch
2
Je ne vois pas comment les gens peuvent supposer que cela f1()évalue toujours ok &&= f(1) mais ne supposera pas que cela évalue toujours ok = ok && f(1). Cela me semble tout aussi probable.
einpoklum
1
Je m'attends en fait v1 += e2à être l'équivalent en sucre syntaxique de la v1 = v1 + e1variable v1 et de l'expression e2. Juste une notation abrégée, c'est tout.
einpoklum