L'opérateur std :: unordered_map [] effectue-t-il une initialisation à zéro pour une clé non existante?


25

Selon cppreference.com, std::map::operator[]pour une valeur non existante, l'initialisation à zéro est effectuée.

Cependant, le même site ne mentionne pas l'initialisation zéro 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?
}

13
@ Ælex vous ne pouvez pas tester de manière fiable si quelque chose est initialisé
idclev 463035818

2
@ Ælex je ne comprends pas vraiment, comment pouvez-vous avoir un non initialisé std::optional?
idclev 463035818

2
@ Ælex il n'y a aucun moyen de tester si un objet est initialisé ou non car toute opération sur un objet non initialisé autre que l'initialisation entraîne un comportement indéfini. Un std::optionalobjet qui ne contient aucune valeur contenue est toujours un objet initialisé.
bolov

2
L'objet value est initialisé en valeur, pas initialisé en zéro. Pour les types scalaires, ce sont les mêmes, mais pour les types de classe, ils sont différents.
aschepler

@bolov J'ai essayé de le tester hier en utilisant gnu 17 et std 17, et bizarrement tout ce que j'ai obtenu était une initialisation nulle. Je pensais std::optional has_valuele tester, mais il échoue, donc je suppose que vous avez raison.
Ælex

Réponses:


13

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 se déplace simplement kdanstry_emplace et est par ailleurs identique)

Si un élément existe sous clé kdans la carte, try_emplaceretourne alors un itérateur à cet élément et false. Sinon, try_emplaceinsère un nouvel élément sous la clé k, et renvoie un itérateur à celui-ci 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_­typeconstruit avecpiecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...)

(la surcharge en prenant une référence rvalue se déplace juste ken forward_­as_­tupleet, encore une fois, est par ailleurs identique)

Puisque value_typec'est un pair<const Key, T> [unord.map.overview] / 2 , cela nous dit que le nouvel élément de carte sera construit comme:

pair<const Key, T>(piecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...));

Puisque argsest vide en venant de operator[], cela revient à notre nouvelle valeur étant construite en tant que membre de l' pairargument no [pairs.pair] / 14 qui est l'initialisation directe [class.base.init] / 7 d'une valeur de type Tusing ()comme initialiseur qui se résume à l'initialisation de la valeur [dcl.init] /17.4 . L'initialisation de la valeur d'un intest une initialisation nulle [dcl.init] / 8 . Et l'initialisation nulle de an intinitialise naturellement cela intà 0 [dcl.init] / 6 .

Alors oui, votre code est garanti pour retourner 0…


21

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, la valeurint est 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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.