web-dev-qa-db-fra.com

Cette fonction a-t-elle des valeurs de retour explicites sur tous les chemins de contrôle?

J'ai une fonction d'étape Heaviside centrée sur l'unité pour tout type de données, que j'ai encodée en utilisant:

template <typename T>
int h1(const T& t){
   if (t < 1){
       return 0;
   } else if (t >= 1){
       return 1;
   }
}

Dans la revue de code, mon critique m'a dit qu'il n'y a pas de retour explicite sur tous les chemins de contrôle. Et le compilateur ne m'avertit pas non plus. Mais je ne suis pas d'accord; les conditions s'excluent mutuellement. Comment gérer cela?

61

Cela dépend de la façon dont le modèle est utilisé. Pour un int, ça va.

Mais, si t est un type à virgule flottante IEEE754 double avec une valeur définie sur NaN, ni t < 1 ni t >= 1 sont true et donc le contrôle du programme atteint la fin du bloc if! Cela provoque le retour de la fonction sans valeur explicite; dont le comportement n'est pas défini.

(Dans un cas plus général, où T surcharge le < et >= opérateurs de manière à ne pas couvrir toutes les possibilités, le contrôle du programme atteindra la fin du bloc if sans explicite return.)

La morale de l'histoire ici est de décider quelle branche doit être la branche par défaut, et d'en faire le cas else.

230
Bathsheba

Ce n'est pas parce que le code est correct qu'il ne peut pas être meilleur. L'exécution correcte est la première étape de qualité, pas la dernière.

if (t < 1) {
    return 0;
} else if (t >= 1){
    return 1;
}

Ce qui précède est "correct" pour tout type de données de t qui a un comportement sain pour < et >=. Mais ça:

if (t < 1) {
    return 0;
}
return 1;

Il est plus facile de voir en inspectant que chaque cas est couvert et évite complètement la deuxième comparaison inutile (que certains compilateurs n'ont peut-être pas optimisée). Le code n'est pas seulement lu par les compilateurs, mais par les humains, y compris vous dans 10 ans. Donnez aux humains une pause et écrivez plus simplement pour leur compréhension.

13
Lee Daniel Crocker

Comme indiqué, certains numéros spéciaux peuvent être à la fois < et >=, donc votre critique a tout simplement raison.

La question est: qu'est-ce qui vous a donné envie de le coder comme ça en premier lieu? Pourquoi envisagez-vous même de rendre la vie si difficile pour vous-même et pour les autres (les personnes qui ont besoin de maintenir votre code)? Juste le fait que vous êtes assez intelligent pour déduire que < et >= devrait couvrir tous les cas ne signifie pas que vous devez rendre le code plus complexe que nécessaire. Ce qui vaut pour la physique vaut aussi pour le code: rendre les choses aussi simples que possible, mais pas plus simples (je crois qu'Einstein l'a dit).

Penses-y. Qu'essayez-vous de réaliser? Doit être quelque chose comme ceci: 'Retourne 0 si l'entrée est inférieure à 1, retourne 1 sinon.' Ce que vous avez fait, c'est ajouter de l'intelligence en disant ... oh mais cela signifie que je retourne 1 si t est supérieur ou égal à 1. Ce type de "x implique inutile" nécessite un travail de réflexion supplémentaire de la part du responsable. Si vous pensez que c'est une bonne chose, je vous conseille de faire vous-même quelques années de maintenance du code.

Si c'était mon avis, je ferais une autre remarque. Si vous utilisez une instruction "if", vous pouvez essentiellement faire tout ce que vous voulez dans toutes les branches. Mais dans ce cas, vous ne faites "rien". Tout ce que vous voulez faire, c'est retourner 0 ou 1 selon que t <1 ou non. Dans ces cas, je pense que l'instruction '?:' Est beaucoup meilleure et plus lisible que l'instruction if. Ainsi:

return t<1 ? 0 : 1;

Je connais le ?: l'opérateur est interdit dans certaines entreprises, et je trouve que c'est une chose horrible à faire. ?: correspond généralement beaucoup mieux aux spécifications et peut rendre le code beaucoup plus facile à lire (s'il est utilisé avec précaution) ...

8
Bert Bril