Je suis en désaccord avec certains collègues sur le code suivant:
int foo ( int a, int b )
{
return b > 0 ? a / b : a;
}
Ce code présente-t-il un comportement indéfini?
EDIT: le désaccord a commencé à partir de ce qui semble être un bogue dans un compilateur d'optimisation trop désireux, où le b > 0
la vérification a été optimisée.
Non.
Citations de N4140:
§5.16 [expr.cond]/1
Groupe d'expressions conditionnelles de droite à gauche. La première expression est convertie contextuellement en bool. Il est évalué et s'il est vrai, le résultat de l'expression conditionnelle est la valeur de la deuxième expression, sinon celle de la troisième expression. ne seule desles deuxième et troisième expressions sont évaluées.
Plus loin:
§5 [expr]/4
Si, lors de l'évaluation d'une expression, le résultat n'est pas défini mathématiquement ou n'est pas dans la plage de valeurs représentables pour son type, le comportement n'est pas défini.
Cela ne se produit clairement pas ici. Le même paragraphe mentionne explicitement la division par zéro dans une note et, bien qu'il ne soit pas normatif, il rend encore plus clair que cela est pertinent dans cette situation:
[Remarque: la plupart des implémentations existantes de C++ ignorent les débordements d'entiers. Le traitement de la division par zéro, formant un reste à l'aide d'un diviseur nul, et toutes les exceptions à virgule flottante varient selon les machines et sont généralement ajustables par une fonction de bibliothèque. —Fin note]
Il existe également des preuves circonstancielles renforçant le point ci-dessus: l'opérateur conditionnel est utilisé pour rendre le comportement non défini de manière conditionnelle.
§8.5 [dcl.init] /12.3
int f(bool b) { unsigned char c; unsigned char d = c; // OK, d has an indeterminate value int e = d; // undefined behavior return b ? d : 0; // undefined behavior if b is true }
Dans l'exemple ci-dessus, en utilisant d
pour initialiser int
(ou autre chose que unsigned char
) n'est pas défini. Pourtant, il est clairement indiqué que l'UB ne se produit que si la branche UB est évaluée.
Sortir du point de vue du juriste: si cela pouvait être UB, alors n'importe quelle division pourrait être traitée comme UB, puisque le diviseur pourrait être 0. Ce n'est pas l'esprit de la règle.
Il n'y a aucun moyen de diviser par zéro dans l'exemple de code. Lorsque le processeur exécute a / b
, il a déjà vérifié que b > 0
, donc b
est différent de zéro.
Il convient de noter que si a == INT_MIN
et b == -1
, puis a/b
est également un comportement indéfini. Mais cela est de toute façon empêché car la condition est évaluée à false
dans ce cas.
Bien que je ne sois pas vraiment sûr que vous vouliez dire return b != 0 ? a / b : a;
et pas return b > 0 ? a / b : a;
Si b est inférieur à zéro, la division est toujours valide, sauf s'il s'agit de la condition décrite ci-dessus.
Ce code présente-t-il un comportement indéfini?
Non. L'expression
return b > 0 ? a / b : a;
est équivalent à
if(b > 0)
return a/b; // this will be executed only when b is greater than 0
else
return a;
Division effectuée uniquement lorsque b
est supérieur à 0
.
Si c'était UB, il en serait de même
if(a != null && *a == 42)
{
.....
}
Et le séquencement des ifs, ands et ors est clairement conçu pour permettre spécifiquement ce type de construction. Je ne peux pas imaginer que vos collègues contesteraient cela