web-dev-qa-db-fra.com

L'ordre d'itération dans std :: map est-il connu (et garanti par la norme)?

Ce que je veux dire, c'est que nous savons que les éléments de std::map Sont triés en fonction des clés. Alors, disons que les clés sont des entiers. Si j'itère de std::map::begin() à std::map::end() à l'aide de for, la norme garantit-elle que j'itérerai en conséquence à travers les éléments avec les clés, triés par ordre croissant?


Exemple:

std::map<int, int> map_;
map_[1] = 2;
map_[2] = 3;
map_[3] = 4;
for( std::map<int, int>::iterator iter = map_.begin();
     iter != map_.end();
     ++iter )
{
    std::cout << iter->second;
}

Est-ce que cela garantit d’imprimer 234 Ou est-ce que l’implémentation est définie?


Raison réelle: J'ai un std::map Avec les clés int. Dans de très rares situations, j'aimerais parcourir tous les éléments, avec une clé supérieure à une valeur concrète int. Oui, il semble que std::vector Serait le meilleur choix, mais remarquez mes "situations très rares".


EDIT : Je sais que les éléments de std::map sont triés. Inutile de le préciser (pour la plupart des réponses ici) . Je l'ai même écrit dans ma question.
Je posais des questions sur les itérateurs et la commande lorsque je parcourais un conteneur. Merci @Kerrek SB pour la réponse.

145
Kiril Kirov

Oui, c'est garanti. De plus, *begin() vous donne le plus petit et *rbegin() le plus grand élément, comme déterminé par l'opérateur de comparaison, ainsi que deux valeurs de clé a et b pour dont l'expression !compare(a,b) && !compare(b,a) est vraie sont considérés comme égaux. La fonction de comparaison par défaut est std::less<K>.

L’ordre n’est pas un bonus, mais un aspect fondamental de la structure de données, car il permet de déterminer quand deux clés sont identiques (par la règle ci-dessus) et d’effectuer une recherche efficace (essentiellement une méthode binaire). recherche, qui présente une complexité logarithmique dans le nombre d'éléments).

162
Kerrek SB

Ceci est garanti par les exigences du conteneur associatif dans la norme C++. Par exemple. voir 23.2.4/10 en C++ 11:

 La propriété fondamentale des itérateurs de conteneurs associatifs est qu'ils 
 Parcourent les conteneurs dans l'ordre non décroissant des clés, où 
 Non décroissant est défini par la comparaison utilisée. pour les construire. 
 Pour deux itérateurs déréférencables i et j tels que la distance de i à j soit 
 positive, 
 value_comp (* j, * i) == faux 

et 23.2.4/11

 Pour les conteneurs associatifs avec des clés uniques, la condition la plus forte est vérifiée, 
 Value_comp (* i, * j)! = False. 
41

Je pense qu'il y a une confusion dans les structures de données.

Dans la plupart des langues, un map est simplement un conteneur associatif: il mappe une clé sur une valeur. Dans les "nouvelles" langues, ceci est généralement réalisé en utilisant une carte de hachage, donc aucun ordre n'est garanti.

En C++, cependant, il n'en est rien:

  • std::map est un conteneur associatif trié
  • std::unordered_map est un conteneur associatif basé sur une table de hachage introduit en C++ 11

Donc, afin de clarifier les garanties sur la commande.

En C++ 03:

  • std::set, std::multiset, std::map et std::multimap sont garantis à commander en fonction des clés (et du critère fourni)
  • dans std::multiset et std::multimap, la norme n'impose aucune garantie d'ordre à des éléments équivalents (c'est-à-dire, ceux qui se comparent égaux)

En C++ 11:

  • std::set, std::multiset, std::map et std::multimap sont garantis à commander en fonction des clés (et du critère fourni)
  • dans std::multiset et std::multimap, la norme impose que les éléments équivalents (ceux qui se comparent égaux) soient ordonnés en fonction de leur ordre d'insertion (premier inséré en premier)
  • std::unordered_* _ Les conteneurs ne sont pas commandés, comme leur nom l’indique. Plus particulièrement, l'ordre des éléments peut changer lorsque le conteneur est modifié (lors de l'insertion/suppression).

Lorsque la norme dit que les éléments sont ordonnés d'une manière, cela signifie que:

  • lors de l'itération, vous voyez les éléments dans l'ordre défini
  • lors d'une itération inverse, vous voyez les éléments dans l'ordre inverse

J'espère que cela dissipe toute confusion.

29
Matthieu M.

Est-ce que cela garantit d’imprimer 234 ou si sa mise en œuvre est définie?

Oui, std::map est un conteneur trié, commandé par le Key avec le Comparator fourni. Donc c'est garanti.

Je voudrais aller parcourir tous les éléments, avec la clé, supérieure à une valeur int concrète.

C'est sûrement possible.

4
jpalecek

Oui ... les éléments d'un std::map Ont un ordre faible strict, ce qui signifie qu'ils seront composés d'un ensemble (c'est-à-dire qu'il n'y aura pas de répétition de clés "égales") et d'égalité. est déterminé en testant sur deux clés A et B quelconques, que si la clé A n'est pas inférieure à la clé B et que B n'est pas inférieure à A, la clé A est égale à la clé B.

Cela étant dit, vous ne pouvez pas trier correctement les éléments d'un std::map Si l'ordre faible de ce type est ambigu (dans votre cas, vous utilisez des entiers comme type de clé, ce n'est pas un problème) . Vous devez être capable de définir une opération définissant un ordre total sur le type que vous utilisez pour les clés de votre std::map. Sinon, vous n'aurez qu'un ordre partiel pour vos éléments, ou poset, qui a une propriété où A peut ne pas être comparable à B. Ce qui se passera généralement dans ce scénario est que vous pourrez insérer les paires clé/valeur, mais que vous risquez de vous retrouver avec des paires clé/valeur en double si vous parcourez toute la carte./ou détectez des paires clé/valeur "manquantes" lorsque vous essayez d'effectuer une std::map::find() d'une paire clé/valeur spécifique dans la carte.

3
Jason