Dans le cas particulier d'une carte, les anciennes options n'étaient que deux: operator[]
et insert
(différentes saveurs deinsert
). Je vais donc commencer à les expliquer.
Le operator[]
est un opérateur de recherche ou d'ajout . Il essaiera de trouver un élément avec la clé donnée à l'intérieur de la carte, et s'il existe, il renverra une référence à la valeur stockée. Si ce n'est pas le cas, il créera un nouvel élément inséré en place avec l'initialisation par défaut et retournera une référence à celui-ci.
La insert
fonction (dans la saveur d'élément unique) prend un value_type
( std::pair<const Key,Value>
), elle utilise la clé ( first
membre) et essaie de l'insérer. Parce std::map
que ne permet pas les doublons s'il y a un élément existant, il n'insérera rien.
La première différence entre les deux est que operator[]
doit être en mesure de construire une initialisation par défaut la valeur , et il est donc inutilisable pour les types de valeurs qui ne peuvent être réinitialisés par défaut. La deuxième différence entre les deux est ce qui se passe lorsqu'il y a déjà un élément avec la clé donnée. La insert
fonction ne modifiera pas l'état de la carte, mais retournera à la place un itérateur à l'élément (et un false
indiquant qu'il n'a pas été inséré).
m[5] = 10;
m.insert(std::make_pair(5,15));
Dans le cas de insert
l'argument est un objet de value_type
, qui peut être créé de différentes manières. Vous pouvez le construire directement avec le type approprié ou passer n'importe quel objet à partir duquel le value_type
peut être construit, ce qui std::make_pair
entre en jeu, car il permet de créer simplement des std::pair
objets, même si ce n'est probablement pas ce que vous voulez ...
L'effet net des appels suivants est similaire :
K t; V u;
std::map<K,V> m;
m.insert( std::pair<const K,V>(t,u) );
m.insert( std::map<K,V>::value_type(t,u) );
m.insert( std::make_pair(t,u) );
Mais ce ne sont pas vraiment les mêmes ... [1] et [2] sont en fait équivalents. Dans les deux cas, le code crée un objet temporaire du même type ( std::pair<const K,V>
) et le transmet à la insert
fonction. La insert
fonction créera le nœud approprié dans l'arborescence de recherche binaire, puis copiera la value_type
partie de l'argument vers le nœud. L'avantage de l'utilisation value_type
est que, eh bien, correspondvalue_type
toujours , vous ne pouvez pas mal saisir le type de value_type
std::pair
arguments!
La différence est dans [3]. La fonction std::make_pair
est une fonction modèle qui créera un fichier std::pair
. La signature est:
template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );
Je n'ai intentionnellement pas fourni les arguments du modèle std::make_pair
, car c'est l'usage courant. Et l'implication est que les arguments du modèle sont déduits de l'appel, dans ce cas être T==K,U==V
, donc l'appel à std::make_pair
retournera un std::pair<K,V>
(notez le manquant const
). La signature exige value_type
que ce soit proche mais différent de la valeur renvoyée par l'appel à std::make_pair
. Comme il est suffisamment proche, il créera un temporaire du type correct et la copie l'initialisera. Cela sera à son tour copié sur le nœud, créant un total de deux copies.
Cela peut être résolu en fournissant les arguments du modèle:
m.insert( std::make_pair<const K,V>(t,u) );
Mais cela est toujours sujet aux erreurs de la même manière que la saisie explicite du type dans le cas [1].
Jusqu'à présent, nous avons différentes façons d'appeler insert
qui nécessitent la création de l' value_type
externe et la copie de cet objet dans le conteneur. Vous pouvez également utiliser operator[]
si le type est constructible et assignable par défaut (se concentrant intentionnellement uniquement dans m[k]=v
), et il nécessite l'initialisation par défaut d'un objet et la copie de la valeur dans cet objet.
En C ++ 11, avec des modèles variadiques et une transmission parfaite, il existe une nouvelle façon d'ajouter des éléments dans un conteneur au moyen de la mise en place (création sur place). Les emplace
fonctions des différents conteneurs font fondamentalement la même chose: au lieu d'obtenir une source à partir de laquelle copier dans le conteneur, la fonction prend les paramètres qui seront transmis au constructeur de l'objet stocké dans le conteneur.
m.emplace(t,u);
Dans [5], le std::pair<const K, V>
n'est pas créé et passé à emplace
, mais plutôt des références à l' objet t
et u
sont passées à ce emplace
qui les transmet au constructeur du value_type
sous - objet à l'intérieur de la structure de données. Dans ce cas, aucune copie de std::pair<const K,V>
n'est effectuée, ce qui est l'avantage par emplace
rapport aux alternatives C ++ 03. Comme dans le cas, insert
il ne remplacera pas la valeur de la carte.
Une question intéressante à laquelle je n'avais pas pensé est de savoir comment emplace
mettre en œuvre une carte, et ce n'est pas un problème simple dans le cas général.