Dependendo de qual sobrecarga estamos falando, std::unordered_map::operator[]é equivalente a [unord.map.elem]
T& operator[](const key_type& k)
{
return try_emplace(k).first->second;
}
(a sobrecarga tendo uma referência rvalue apenas se move kpara try_emplacee é de outro modo idêntica)
Se um elemento existir sob a chave kno mapa, try_emplaceretornará um iterador para esse elemento e false. Caso contrário, try_emplaceinsere um novo elemento sob a chave ke retorna um iterador para isso e true [unord.map.modifiers] :
template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
Interessante para nós é o caso de ainda não haver nenhum elemento [unord.map.modifiers] / 6 :
Caso contrário, insere um objeto do tipo value_typeconstruído compiecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...)
(a sobrecarga que toma uma referência rvalue apenas se move kpara forward_as_tuplee, novamente, é idêntica)
Como value_typeé um pair<const Key, T> [unord.map.overview] / 2 , isso nos diz que o novo elemento do mapa será construído como:
pair<const Key, T>(piecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...));
Como argsestá vazio quando vem de operator[], isso se resume ao nosso novo valor sendo construído como membro dos pairargumentos from from [pairs.pair] / 14, que é a inicialização direta [class.base.init] / 7 de um valor do tipo Tusing ()como inicializador, que se resume à inicialização de valor [dcl.init] /17.4 . A inicialização do valor de um inté inicialização zero [dcl.init] / 8 . E a inicialização zero de um intnaturalmente inicializa isso intpara 0 [dcl.init] / 6 .
Então, sim, seu código está garantido para retornar 0…