Stateful foncteurs & STL: Comportement indéfini

Je suis ce tutoriel sur les objects Fonction

Copie-pâtes ci-dessous:

Je suis incapable de comprendre ce qui suit:

Les prédicats doivent toujours être implémentés en tant qu’objects de fonction sans état pour éviter des résultats inattendus. Il n’y a aucune garantie sur la fréquence à laquelle un algorithme pourrait copier en interne le prédicat. Ainsi, le fait d’avoir des prédicats implémentés en tant qu’objects fonction avec état peut avoir des résultats inexacts.

L’exemple est le suivant:

#include  #include  #include  #include  class predicate { public: predicate(int condition) : condition_(condition), state_(0) {} bool operator()(int) { return ++state_ == condition_; } private: int condition_; int state_; }; int main() { std::vector vec; vec.push_back(1); vec.push_back(2); vec.push_back(3); vec.push_back(4); vec.push_back(5); predicate p(2); std::vector::iterator pos = std::remove_if(vec.begin(), vec.end(), p); vec.erase(pos, v.end()); std::copy(vec.begin(), vec.end(), std::ostream_iterator(std::cout, " ")); return 0; } 

Si je le comprends bien, il tente de supprimer l’élément marqué 2 dans le vecteur. L’algorithme remove_if renvoie une nouvelle extrémité du conteneur et tente de tout effacer.

Sortie:

 1 3 5 

De toute évidence, non seulement le deuxième élément a été supprimé, mais également le quasortingème. La réponse à cette curiosité est simplement que l’algorithme utilisé ‘remove_if’ copie en interne le prédicat pendant son exécution. Et cette copie interne crée un nouvel object de prédicat contenant son état d’origine.

Bien que je puisse lire ce qui semble se passer, je suis incapable d’imaginer ce qui se passe dans les coulisses et qui a même marqué le 4ème élément à déplacer au bout du conteneur. Cela a-t-il à voir avec un algorithme à passe unique ou à passe multiple? (Je vous serais également reconnaissant si quelqu’un pouvait m’indiquer comment en déduire la même chose)

Sur une note de côté, si je commente l’effacement & note la sortie.

 1 3 5 4 5 

Qu’est-ce qui cause la corruption du conteneur?

Le sens de cette citation doit être pris au pied de la lettre. Pour la majorité des algorithmes STL, vous ne devez pas implémenter votre foncteur de prédicat tel qu’il ait un état observable (“effets secondaires” de AKA), car:

  • l’ordre d’itération sur le conteneur n’est pas défini,
  • l’algorithme est libre de faire des copies du foncteur,
  • l’algorithme peut dépendre de l’état sans état afin de ne pas corrompre le contenu du conteneur ni provoquer un crash.

Le moyen le plus simple de vous imposer cela est de définir operator() tant que const .

Il existe des exceptions, telles que for_each , pour lesquelles aucune des for_each ci-dessus ne s’applique. Vous êtes libre d’utiliser des foncteurs avec état ici. Pour plus d’informations, consultez cet excellent article: http://drdobbs.com/cpp/184403769 .

En coulisse, les auteurs de votre implémentation STL sont libres d’écrire les remove_if (et autres) de la manière qui leur remove_if , à condition qu’ils respectent les exigences du standard. Il n’y a pas vraiment de raison de s’inquiéter de la raison pour laquelle vous obtenez le comportement que vous observez, mis à part le fait de reconnaître qu’il n’est pas défini. Si vous voulez connaître les détails, je voudrais simplement jeter un coup d’œil au code de remove_if dans l’implémentation STL que vous utilisez.

Quant à votre note de côté; ce n’est pas une “corruption”, c’est simplement un artefact de la façon remove_if fonctionne remove_if (cela se produirait même pour un prédicat valide). La seule exigence est que tous les éléments à gauche de pos soient valides (car ils doivent être conservés). Il n’y a aucune exigence sur quels éléments existent à partir de pos (voir ici ). (Le chapitre 32 de ” Effective STL ” de Scott Meyers explique bien pourquoi remove_if (et ainsi de suite) se comporte de la sorte).