55 votes

Qu'est-ce qu'une double évaluation et pourquoi devrait-elle être évitée?

Je lisais cela en C ++ en utilisant des macros comme

 #define max(a,b) (a > b ? a : b)
 

peut entraîner une «double évaluation». Quelqu'un peut-il me donner un exemple de la double évaluation et de la raison pour laquelle elle est mauvaise?

PS: Étonnamment, je n'ai trouvé aucune explication détaillée lors de la recherche de Google à l'exception d'un exemple dans Clojure (que je ne comprends pas).

75voto

WhiZTiM Points 3351

Imaginez que vous avez écrit ceci:

#define Max(a,b) (a < b ? b : a)

int x(){ turnLeft();   return 0; }
int y(){ turnRight();  return 1; }

ensuite appelé comme ceci:

auto var = Max(x(), y());

Savez-vous que turnRight() sera exécutée deux fois? Cette macro, Max étendra à:

auto var = (x() < y() ? y() : x());

Après l'évaluation de la condition x() < y(), le programme prend alors le nécessaire branche entre y() : x(): dans notre cas true, qui demande y() pour la seconde fois. Voir en Direct Sur Coliru.

Simplement mis, en passant d'une expression comme argument à ta fonction comme macro, Max sera potentiellement évaluer cette expression deux fois, parce que l'expression sera répété à l'endroit où le paramètre de la macro, il prend, est utilisé dans la macro définition. Rappelez-vous, les macros sont traitées par le préprocesseur.


Donc, la ligne du bas est, ne pas utiliser des macros pour définir une fonction (ou d'une expression dans ce cas) tout simplement parce que vous voulez qu'il soit générique, alors qu'il peut être fait à l'aide d'une fonction de modèles

PS: C++ a un std::max fonction de modèle.

24voto

Franck Points 1510

a et b apparaissent deux fois dans la définition de la macro. Donc, si vous l'utilisez avec des arguments ayant des effets secondaires, ceux-ci sont exécutés deux fois.

 max(++i, 4);
 

retournera 6 si i = 4 avant l'appel. Comme ce n'est pas le comportement attendu, vous devriez préférer les fonctions en ligne pour remplacer des macros telles que max .

20voto

selbie Points 20267

Prenons l'expression suivante:

 x = max(Foo(), Bar());

Foo et Bar sont comme ceci:

int Foo()
{
    // do some complicated code that takes a long time
    return result;
}

int Bar()
{
   global_var++;
   return global_var;
}

Puis, à l'origine, max expression est élargi comme:

 Foo() > Bar() ? Foo() : Bar();

Dans les deux cas, Foo Bar ou va exécutée deux fois. Prenant ainsi plus longtemps que nécessaire ou la modification du programme de l'état plus que le nombre prévu de fois. Dans mon simple Bar exemple, il ne renvoie pas la même valeur constante.

8voto

kfsone Points 7375

Le langage de macro dans le C et le C++ est traitée par un analyseur dédié à la "pré-traitement" de la scène; les jetons sont traduits et la sortie est ensuite introduit dans le flux d'entrée de l'analyseur de bon. #define et #include jetons sont pas reconnus par le C ou C++ analyseurs eux-mêmes.

Ceci est important parce que cela signifie que quand une macro est dit "étendu", il signifie littéralement qu'. Compte tenu de

#define MAX(A, B) (A > B ? A : B)

int i = 1, j = 2;
MAX(i, j);

ce que l'analyseur C++ voit est

(i > j ? i : j);

Cependant, si on utilise la macro avec quelque chose de plus complexe, de la même expansion qui se passe:

MAX(i++, ++j);

est élargi à

(i++ > ++j ? i++ : ++j);

Si l'on fait passer quelque chose qui fait un appel de la fonction:

MAX(f(), g());

cela permettra d'élargir à

(f() > g() ? f() : g());

Si le compilateur/optimiseur peut démontrer qu' f() n'a pas d'effets secondaires, alors il va traiter cela comme

auto fret = f();
auto gret = g();
(fret > gret) ? fret : gret;

Si il ne peut pas, alors il va falloir appeler f() et g() deux fois, par exemple:

#include <iostream>

int f() { std::cout << "f()\n"; return 1; }
int g() { std::cout << "g()\n"; return 2; }

#define MAX(A, B) (A > B ? A : B)

int main() {
    MAX(f(), g());
}

Démonstration en direct: http://ideone.com/3JBAmF

De même, si nous appelions un extern de la fonction, l'optimiseur peut ne pas être en mesure d' éviter d'appeler la fonction deux fois.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X