Dependiendo de qué sobrecarga estamos hablando, std::unordered_map::operator[]es equivalente a [unord.map.elem]
T& operator[](const key_type& k)
{
return try_emplace(k).first->second;
}
(la sobrecarga de tomar un valor p-referencia sólo se mueve ken try_emplacey es por lo demás idéntico)
Si existe un elemento bajo la clave ken el mapa, entonces try_emplacedevuelve un iterador a ese elemento y false. De lo contrario, try_emplaceinserta un nuevo elemento debajo de la clave ky devuelve un iterador a eso y true [unord.map.modifiers] :
template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
Interesante para nosotros es el caso de que todavía no haya ningún elemento [unord.map.modifiers] / 6 :
De lo contrario, inserta un objeto de tipo value_typeconstruido conpiecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...)
(la sobrecarga de tomar un valor p-referencia sólo se mueve ken forward_as_tupley, de nuevo, es por lo demás idéntico)
Como value_typees un pair<const Key, T> [unord.map.overview] / 2 , esto nos dice que el nuevo elemento del mapa se construirá como:
pair<const Key, T>(piecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...));
Como argsestá vacío cuando viene de operator[], esto se reduce a nuestro nuevo valor que se construye como miembro de los pairargumentos sin par [pair.pair] / 14, que es la inicialización directa [class.base.init] / 7 de un valor de tipo Tusando ()como inicializador que se reduce a inicialización de valor [dcl.init] /17.4 . La inicialización del valor de un intes inicialización cero [dcl.init] / 8 . Y la inicialización cero de un intnaturalmente inicializa eso inta 0 [dcl.init] / 6 .
Entonces sí, su código está garantizado para devolver 0 ...