Выполняет ли 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.

Другие вопросы по тегам