Несмотря на то, что реализация Стандартной библиотеки может делать все, что им нравится, интересно также, как ваши предположения - и те, которые выражены в нескольких комментариях, соответствуют реальной реализации.
Я мог бы воспроизвести ваш результат без «0 1 2 3 4» с помощью GCC, но только добавив map.reserve(6)
или более (странно, 5 произведенных «4 0 1 2 3»).
Подробности ниже просто объяснить поведение версии GCC я посмотрел на ....
рыть для объяснения, я первый здравомыслие проверил, содержали ли логические ковши хэш-FUNCTION- подразумеваемое содержание:
for (size_t i = 0; i < map.bucket_count(); ++i)
{
std::cout << '[' << i << ']';
for (auto it = map.begin(i); it != map.end(i); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
И, действительно, они сделали:
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4
[5]
[6]
Итак, комментарий, согласно которому «Стандартная библиотека может свободно применять любую обратимую функцию поверх вашей хеш-функции, и никакой гарантии о заказе не предоставляется» - хотя это правда - это не то, что здесь происходит.
копания в заголовки стандартной библиотеки, я нашел причину в документации bits/hashtable.h
«s:
* Here's _Hashtable data structure, each _Hashtable has:
* - _Bucket[] _M_buckets
* - _Hash_node_base _M_before_begin
* - size_type _M_bucket_count
* - size_type _M_element_count
*
* with _Bucket being _Hash_node* and _Hash_node constaining:
* - _Hash_node* _M_next
* - Tp _M_value
* - size_t _M_code if cache_hash_code is true
*
* In terms of Standard containers the hastable is like the aggregation of:
* - std::forward_list<_Node> containing the elements
* - std::vector<std::forward_list<_Node>::iterator> representing the buckets
*
* The non-empty buckets contain the node before the first bucket node. This
* design allow to implement something like a std::forward_list::insert_after
* on container insertion and std::forward_list::erase_after on container
* erase calls. _M_before_begin is equivalent to
* std::foward_list::before_begin. Empty buckets are containing nullptr.
* Note that one of the non-empty bucket contains &_M_before_begin which is
* not a derefenrenceable node so the node pointers in buckets shall never be
* derefenrenced, only its next node can be.
*
* Walk through a bucket nodes require a check on the hash code to see if the
* node is still in the bucket. Such a design impose a quite efficient hash
* functor and is one of the reasons it is highly advise to set
* __cache_hash_code to true.
*
* The container iterators are simply built from nodes. This way incrementing
* the iterator is perfectly efficient independent of how many empty buckets
* there are in the container.
*
* On insert we compute element hash code and thanks to it find the bucket
* index. If the element must be inserted on an empty bucket we add it at the
* beginning of the singly linked list and make the bucket point to
* _M_before_begin. The bucket that used to point to _M_before_begin, if any,
* is updated to point to its new before begin node.
Таким образом, хэш-таблицы, лежащие в основе unordered_set
организована с значений в односвязный список, и ведра вектор итераторов в этот список, а не обычно предусматриваемый vector<forward_list<>>
.
Как вставить элементы, они будут в прямом списке на передней, и это тот список, который вы итерацию при переходе от begin()
к end()
, без какого-либо участия vector
итераторов которых заказные соответствует к значениям хэша.
код here иллюстрирует, как итерация возвращает значения в обратном порядке вставки, независимо от хешей/столкновений - тех пор, пока есть достаточно мест reserve()
д до фронта, чтобы избежать перефразируя.
Я думаю, что вам, по крайней мере, нужно указать используемый компилятор – Petr
clang и gcc. Почему это влияет на это? –
Я сомневаюсь, что стандарт накладывает какой-либо особый способ обработки хэша и, следовательно, любого определенного порядка значений, хранящихся в 'unordered_set', поэтому каждый компилятор может свободно выбирать любой подход. – Petr