Выполняет ли std::unordered_map operator[] нулевую инициализацию для несуществующего ключа?
Согласно cppreference.com, std::map::operator[]
для несуществующего значения выполняет нулевую инициализацию.
Однако на том же сайте не упоминается инициализация нуля для std::unordered_map::operator[]
, за исключением того, что у него есть пример, который полагается на это.
Конечно, это всего лишь справочный сайт, а не стандартный. Итак, код ниже в порядке или нет?
#include <unordered_map>
int main() {
std::unordered_map<int, int> map;
return map[42]; // is this guaranteed to return 0?
}
2 ответа
В зависимости от того, о какой перегрузке идет речь, std::unordered_map::operator[]
эквивалентно [unord.map.elem]
T& operator[](const key_type& k)
{
return try_emplace(k).first->second;
}
(перегрузка, принимающая rvalue-ссылку, просто перемещает k
в try_emplace
и в остальном идентичен)
Если элемент существует под ключом k
на карте, тогда try_emplace
возвращает итератор этому элементу и false
. Иначе,try_emplace
вставляет новый элемент под ключ k
, и возвращает ему итератор и true
[unord.map.modifiers]:
template <class... Args>
pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
Для нас интересен случай, когда элемента еще нет [unord.map.modifiers] / 6:
В противном случае вставляет объект типа
value_type
построен сpiecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...)
(перегрузка, принимающая rvalue-ссылку, просто перемещает k
в forward_as_tuple
и, опять же, в остальном идентичен)
поскольку value_type
это pair<const Key, T>
[unord.map.overview] / 2, это говорит нам, что новый элемент карты будет построен как:
pair<const Key, T>(piecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...));
поскольку args
пуст, когда приходит из operator[]
, это сводится к тому, что наше новое значение конструируется как член pair
без аргументов [pair.pair]/14, что является прямой инициализацией [class.base.init]/7 значения типаT
с помощью ()
как инициализатор, который сводится к инициализации значения [dcl.init] /17.4. Инициализация значенияint
это нулевая инициализация [dcl.init] / 8. И нулевая инициализацияint
естественно инициализирует это int
до 0 [dcl.init] / 6.
Так что да, ваш код гарантированно вернет 0…
На сайте, на который вы ссылаетесь, написано:
Когда используется распределитель по умолчанию, это приводит к тому, что ключ создается копией из ключа, а отображаемое значение инициализируется значением.
Так что int
это значение инициализируется:
Эффекты инициализации значения:
[...]
4) в противном случае объект инициализируется нулем
Вот почему результат 0
.