std :: unordered_map :: emplace создание объекта

Я находился в процессе выбора одного из двух способов помещения вещей в unordered_map:

std::unordered_map<Key, Value> map;
map.emplace(
std::piecewise_construct,
std::forward_as_tuple(a),
std::forward_as_tuple(b, c, d));

против

std::unordered_map<Key, DifferentValue> map;
auto& value = map[a];
if (value.isDefaultInitialized())
value = DifferentValue(b, c, d);

Я провел несколько экспериментов, чтобы увидеть, какой из них будет работать лучше, чтобы обнаружить, что при вставке уникальных элементов поведение (как в эффективности) было в основном эквивалентным.

Тем не менее, в случае вставки дублирующих элементов и с учетом того, что построение Value или DifferentValue нетривиально, я с удивлением обнаружил, что emplace создает объект независимо от того, вставит ли он его или нет.

Таким образом, второй метод, похоже, выиграл в этом случае, так как конструктор по умолчанию просто содержит isDefaultInitialized_ (true) и не намного больше.

Для emplace код выглядит так:

... _M_emplace(std::true_type, _Args&&... __args) {
__node_type* __node = _M_allocate_node(std::forward<_Args>(__args)...);
const key_type& __k = this->_M_extract()(__node->_M_v);
...
if (__node_type* __p = _M_find_node(__bkt, __k, __code)) {
_M_deallocate_node(__node);
return std::make_pair(iterator(__p), false);
}
return std::make_pair(_M_insert_unique_node(__bkt, __code, __node), true);
}

Итак, хотя я собираюсь перейти ко второму методу (даже если он требует назначения перемещения и конструкторов перемещения и дополнительных полей), мне было интересно, есть ли хорошее обоснование того, почему emplace создает объект, который он позже игнорирует? То есть должен ли он сначала проверить, нужно ли ему создать объект, и заблаговременно, если он уже существует?

(обратите внимание, что для моего конкретного случая инициализированные элементы по умолчанию не считаются действительными, поэтому вопрос действительно только об emplace)

Для записи я нашел что-то под 23.2.4 таблицы 102:

Effects: Inserts a value_type object t constructed with std::forward<Args>(args)...
if and only if there is no element in the container with key equivalent to the
key of t.

который, я думаю, позволил бы не создавать объект.

3

Решение

На мой взгляд, цитируемая часть стандарта вводит в заблуждение, поскольку предполагает, что объект создается только в том случае, если в контейнере нет соответствующего элемента. Я предполагаю, что они пытаются заявить:

Последствия: Создает value_type объект t с std::forward<Args>(args)..., Вставляет построенный объект t если и только если в контейнере нет такого элемента с ключом, эквивалентным ключу t,

Причина заключается в следующем: реализация функции emplace должен построить t чтобы узнать, существует ли элемент с эквивалентным ключом, потому что реализация должна вызвать гашиш функция и равняется сказуемое. Однако в целом они могут быть вызваны только с объектами типа value_type, не с кортежи используется для построения этих объектов.

Теоретически можно было бы указать устанавливать функция, которая не создает t если уже есть элемент с эквивалентным ключом. Интересно, что нечто подобное будет добавлено в C ++ 14 для std::map::find, Смотрите следующую документацию:

Существует две перегрузки, которые можно использовать с произвольными типами, если только сравнить Функция выполняет некоторые дополнительные требования. Интересно, что такой перегрузки для std::unordered_map,

2

Другие решения


По вопросам рекламы [email protected]