Quel est le meilleur/idiomatiques façon à insérer dans une carte?
J'ai identifié quatre différentes façons d'insérer des éléments dans un std::map
:
std::map<int, int> function;
function[0] = 42;
function.insert(std::map<int, int>::value_type(0, 42));
function.insert(std::pair<int, int>(0, 42));
function.insert(std::make_pair(0, 42));
Qui est le préféré/idiomatiques façon? (Et s'il y a une autre façon, je n'ai pas pensé?)
- Votre carte devrait être appelé "réponses", pas de "fonction"
- Hm? Une fonction est essentiellement une carte entre les deux ensembles.
- semble-Vincent-du commentaire est un peu une blague au sujet de certains livres...
- Il semble contredire l'original-42 ne peut pas simultanément être la réponse à (a) la vie, l'univers et tout, et (b) rien. Mais alors, comment exprimez-vous la vie, l'univers et tout comme un int?
- Vous pouvez exprimer à tout avec un entier assez grand.
- Ah! Godel ne serait pas heureux de votre commentaire! Probablement, vous devez lire (ou relire) le merveilleux en.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach
- La façon dont je vois actuellement, si
42
peut jeter, pour la première version, il n'est pas défini si la carte contient un {0, default_constructed} ou pas. - ou pi...
- Pouvez-vous exprimer quelque chose en termes de nombre où ce nombre doit être plus grand que l'infini ou même plus grand que l'infini à l'infini, de puissance...? Comment parler de quelque chose qui est éternel comme l'Esprit?
- Depuis les Entiers sont dénombrables (aleph-nulle), l'utilisation des Réels, ou mieux encore, un 4-tuple de Complexe
Vous devez vous connecter pour publier un commentaire.
Tout d'abord,
operator[]
etinsert
fonctions de membre ne sont pas fonctionnellement équivalent :operator[]
sera de recherche pour la clé, insérez un par défaut construit valeur si elle ne trouve pas, et de renvoyer une référence qui vous permet d'assigner une valeur. Évidemment, cela peut s'avérer inefficace si lemapped_type
peuvent être initialisée directement au lieu de la valeur par défaut construit et affecté. Cette méthode ne permet pas de déterminer si l'insertion a bien eu lieu ou si vous avez seulement écrasé à la valeur précédemment inséré la cléinsert
fonction membre n'aura aucun effet si la clé est déjà présent dans la carte et, bien qu'il est souvent oublié, renvoie unstd::pair<iterator, bool>
qui peuvent être d'intérêt (notamment pour déterminer si l'insertion a été effectivement le cas).De tous les possibilités d'appel
insert
, tous les trois sont presque équivalent. Pour rappel, voyonsinsert
signature dans la norme :Alors, comment sont les trois appels différents ?
std::make_pair
s'appuie sur l'argument de modèle de déduction et pourrait (et dans ce cas sera) produire quelque chose de différent du réelvalue_type
de la carte, qui va nécessiter un appel supplémentaire àstd::pair
modèle constructeur afin de convertirvalue_type
(ie : ajouterconst
àfirst_type
)std::pair<int, int>
exige aussi un appel supplémentaire au modèle constructeur destd::pair
afin de convertir le paramètrevalue_type
(ie : ajouterconst
àfirst_type
)std::map<int, int>::value_type
laisse absolument aucune place pour le doute, car il est directement le type de paramètre attendu par leinsert
fonction membre.En fin de compte, je voudrais éviter d'utiliser
operator[]
lorsque l'objectif est d'insérer, à moins qu'il n'y a aucun coût supplémentaire par défaut-de la construction et de l'affectation de lamapped_type
, et que je ne se soucient pas de déterminer si une nouvelle clé a effectivement insérée. Lors de l'utilisation deinsert
, la construction d'unvalue_type
est probablement la voie à suivre.Comme du C++11, vous avez deux grandes options supplémentaires. Tout d'abord, vous pouvez utiliser
insert()
avec initialisation de la liste de syntaxe:C'est fonctionnellement équivalent à
mais beaucoup plus concis et lisible. Comme d'autres réponses ont noté, ce qui a plusieurs avantages par rapport aux autres formes:
operator[]
approche nécessite la mappé type à être cédés, ce qui n'est pas toujours le cas.operator[]
approche peut remplacer les éléments existants, et ne vous donne aucun moyen de dire si ce qui s'est passé.insert
que vous liste impliquer implicite de conversion de type, ce qui peut ralentir votre code vers le bas.L'inconvénient majeur est que cette forme est utilisée pour exiger la clé et la valeur d'être copiable, donc il ne serait pas travailler avec par exemple, une carte avec
unique_ptr
valeurs. Qui a été fixée dans la norme, mais le correctif peut ne pas avoir atteint votre bibliothèque standard de mise en œuvre encore.Deuxièmement, vous pouvez utiliser le
emplace()
méthode:C'est plus concis que l'une des formes de
insert()
, fonctionne très bien avec déplacez-que des types commeunique_ptr
, et, théoriquement, peut-être un peu plus efficace (bien qu'un décent compilateur d'optimiser loin la différence). Le seul inconvénient majeur est qu'il peut surprendre vos lecteurs un peu, depuisemplace
méthodes ne sont généralement pas utilisés de cette façon.La première version:
peut ou peut ne pas insérer la valeur 42 dans la carte. Si la clé
0
existe, alors il va céder à 42 de cette clé, en écrasant la valeur de cette clé. Sinon, il insère la paire clé/valeur.L'insérer des fonctions:
d'autre part, de ne rien faire si la clé
0
existe déjà dans la carte. Si la clé n'existe pas, on insère la paire clé/valeur.Les trois insérer des fonctions sont presque identiques.
std::map<int, int>::value_type
est letypedef
pourstd::pair<const int, int>
, etstd::make_pair()
évidemment produit unstd::pair<>
par modèle déduction de la magie. Le résultat final, cependant, devrait être la même pour les versions 2, 3 et 4.Lequel dois-je utiliser? Personnellement, je préfère la version 1; il est concis et "naturel". Bien sûr, si son écrasement comportement n'est pas souhaité, puis je préfère la version 4, car il nécessite moins de frappe que les versions 2 et 3. Je ne sais pas si il y a un seul de facto moyen de l'insertion de paires clé/valeur dans un
std::map
.Une autre façon d'insérer des valeurs dans une carte via l'un de ses constructeurs:
J'ai été l'exécution de certaines comparaisons entre les versions précitées:
S'avère que les différences de temps entre l'insert versions sont minuscules.
Cela donne respectivement pour les versions (j'ai exécuté le fichier 3 fois, d'où le 3 fois de suite les différences pour chaque):
2198 ms, 2078 ms, ms 2072
2290 ms, 2037 ms, 2046 ms
2592 ms, 2278 ms, 2296 ms
2234 ms, 2031 ms, 2027 ms
Par conséquent, les résultats entre les différents insérer les versions peuvent être négligées (n'a pas à effectuer un test d'hypothèse tout de même)!
La
map_W_3[it] = Widget(2.0);
version prend environ 10-15 % de temps en plus pour cet exemple en raison d'une initialisation avec le constructeur par défaut du Widget.En bref,
[]
opérateur est plus efficace pour la mise à jour des valeurs, car il implique l'appel de constructeur par défaut du type de la valeur et en l'affectant une nouvelle valeur, tandis queinsert()
est plus efficace pour l'ajout de valeurs.La cité extrait de Efficace STL: 50 Moyens Spécifiques pour Améliorer Votre Utilisation de la Bibliothèque de modèles Standard par Scott Meyers, Article 24, de ce qui pourrait aider.
Vous pouvez décider de choisir un des génériques de la programmation-la version gratuite de ce, mais le point est que je trouve ce paradigme (différenciation "ajouter" et "mise à jour") extrêmement utile.
Si vous souhaitez remplacer l'élément avec la touche 0
Autrement:
Si vous souhaitez insérer un élément dans std::map - utilisation de l'insert() de la fonction, et si vous voulez trouver de l'élément (par clé) et attribuer certains de il - utiliser l'opérateur[].
Pour simplifier l'insertion d'utiliser boost::attribuer une bibliothèque, comme ceci:
Je viens de changer le problème un peu (carte de chaînes de caractères) pour montrer un autre intérêt de l'insert:
le fait que le compilateur affiche pas d'erreur sur "rancking[1] = 42;" peut avoir des effets dévastateurs !
std::string::operator=(char)
existe, mais ils montrent une erreur pour la dernière parce que le constructeurstd::string::string(char)
n'existe pas. ne pas produire une erreur parce que le C++ toujours interprète librement tout entier de style littéral commechar
, donc ce n'est pas un compilateur bug, mais plutôt une erreur du développeur. En gros, je dis juste que si oui ou non cela introduit un bug dans votre code est quelque chose que vous avez à regarder dehors pour vous-même. BTW, vous pouvez imprimerrancking[0]
et d'un compilateur à l'aide de l'ASCII sera de sortie*
, qui est(char)(42)
.Depuis C++17
std::map
propose deux nouvelles méthodes d'insertion:insert_or_assign()
ettry_emplace()
, comme mentionné aussi dans le commentaire par sp2danny.insert_or_assign()
Fondamentalement,
insert_or_assign()
est une version "améliorée" deoperator[]
. Contrairement àoperator[]
,insert_or_assign()
ne nécessite pas la carte du type de valeur à la valeur par défaut constructible. Par exemple, le code suivant ne compile pas, parce queMyClass
ne possède pas de constructeur par défaut:Toutefois, si vous remplacez
myMap[0] = MyClass(1);
par la ligne suivante, puis le code compile et l'insertion se déroule comme prévu:En outre, similaire à
insert()
,insert_or_assign()
renvoie unepair<iterator, bool>
. La valeur Booléenne esttrue
si l'insertion s'est produite etfalse
si un travail a été fait. L'itérateur points à l'élément qui a été insérée ou mise à jour.try_emplace()
Similaire à ce qui précède,
try_emplace()
est une "amélioration" deemplace()
. Contrairement àemplace()
,try_emplace()
ne modifie pas ses arguments si l'insertion échoue à cause d'une clé déjà existante dans la carte. Par exemple, le code suivant tente de emplace un élément avec une clé qui est déjà stocké dans la carte (voir *):De sortie (au moins pour VS2017 et Coliru):
Comme vous pouvez le voir,
pMyObj
plus de points à l'objet d'origine. Toutefois, si vous remplacezauto [it, b] = myMap2.emplace(0, std::move(pMyObj));
par le code suivant, puis la sortie est différent, parce quepMyObj
reste inchangé:De sortie:
Code sur Coliru
Veuillez noter: j'ai essayé de garder mes explications courtes et simples que possible afin de les adapter à cette réponse. Pour une approche plus précise et complète la description, je recommande la lecture de cet article sur Couramment le C++.