Selon cppreference.com, std::map::operator[]
pour une valeur non existante fait une initialisation nulle.
Cependant, le même site ne mentionne pas d'initialisation nulle pour std::unordered_map::operator[]
, sauf qu'il a un exemple qui s'appuie sur cela.
Bien sûr, ce n'est qu'un site de référence, pas la norme. Alors, le code ci-dessous est-il correct ou non?
#include <unordered_map>
int main() {
std::unordered_map<int, int> map;
return map[42]; // is this guaranteed to return 0?
}
Selon la surcharge dont nous parlons, std::unordered_map::operator[]
Équivaut à [unord.map.elem]
T& operator[](const key_type& k)
{
return try_emplace(k).first->second;
}
(la surcharge prenant une référence rvalue déplace simplement k
dans try_emplace
et est par ailleurs identique)
Si un élément existe sous la clé k
dans la carte, alors try_emplace
Renvoie un itérateur à cet élément et false
. Sinon, try_emplace
Insère un nouvel élément sous la clé k
, et renvoie un itérateur à cela et true
[unord.map.modifiers] :
template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
Intéressant pour nous est le cas où il n'y a pas encore d'élément [unord.map.modifiers]/6 :
Sinon, insère un objet de type
value_type
Construit avecpiecewise_construct, forward_as_Tuple(k), forward_as_Tuple(std::forward<Args>(args)...)
(la surcharge prenant une rvalue-reference déplace simplement k
dans forward_as_Tuple
et, là encore, est par ailleurs identique)
Puisque value_type
Est un pair<const Key, T>
[unord.map.overview]/2 , cela nous indique que le nouvel élément de carte sera construit comme suit:
pair<const Key, T>(piecewise_construct, forward_as_Tuple(k), forward_as_Tuple(std::forward<Args>(args)...));
Puisque args
est vide en venant de operator[]
, Cela revient à notre nouvelle valeur étant construite en tant que membre de pair
sans argument [pairs.pair ]/14 qui est une initialisation directe [class.base.init]/7 d'une valeur de type T
en utilisant ()
Comme initialiseur qui se résume à initialisation de la valeur [dcl.init] /17.4 . L'initialisation de la valeur d'un int
est une initialisation nulle [dcl.init]/8 . Et l'initialisation zéro d'un int
initialise naturellement ce int
à 0 [dcl.init]/6 .
Alors oui, votre code est garanti pour retourner 0…
Sur le site que vous avez lié, il est écrit:
Lorsque l'allocateur par défaut est utilisé, cela entraîne la construction de la copie à partir de la clé et l'initialisation de la valeur mappée.
Donc, le int
est valeur initialisée :
Les effets de l'initialisation de la valeur sont:
[...]
4) sinon, l'objet est initialisé à zéro
C'est pourquoi le résultat est 0
.