Подтвердить что ты не робот

Предоставляет ли карта-вставка гарантия того, что элементы перемещены или не перемещены?

Стандартные контейнеры "map" в С++ позволяют вставлять rvalue:

T x;

std::map<int, T> m;

// m[1];  // populate "1"

auto it = m.insert(std::make_pair(1, std::move(x)));

Вопрос заключается в том, что происходит, когда элемент уже существует, т.е. it->second == false. Был ли элемент x уже "перемещен-из"? Например, если это уникальный указатель, x был reset равным null?

Патентованный ответ в приведенном выше случае - "да", потому что перемещение происходит уже в точке создания пары. Но предположим, теперь я хочу обновить существующее значение, но все равно сохраняю информацию о том, существовало ли значение или нет (поэтому я не могу просто сказать m[1] = std::move(x);). Возможно ли "не перейти от" объекта в этом случае?

В GCC я обнаружил, что следующие работы [Обновление: работает в GCC 4.6, не работает в GCC 4.8]:

auto it = m.insert(std::pair<const int, T &&>(1, std::move(x)));

Но гарантируется ли это, что он не движется?

4b9b3361

Ответ 1

Хотя std::move на самом деле не выполняет никакого перемещения, а также std::make_pair, std::make_pair пересылает свои аргументы в конструктор std::pair, который инициализирует два своих члена из этих аргументов.

Таким образом, ход выполняется в этой точке, прежде чем std::map имеет шанс что-либо сделать. Итак, да, вы закончили "сломанный" ход по уважительной причине.

Вы должны уметь использовать emplace (чтобы пропустить конструкцию пары). Из таблицы 102:

Эффекты: Вставляет объект T T, построенный с помощью std::forward<Args>(args)..., если и только если в контейнере нет элемента с ключом, эквивалентным ключу T.

Очевидно, что библиотека по-прежнему "переадресовывает" в этот момент, поэтому предварительно перемещается, и в вашем случае emplace не будет иметь место, поэтому все выражение должно быть эффективным no-op.

Однако, libstdС++ из У GCC 4.8.0, похоже, есть ошибка в этом отношении: emplace вызывает _M_emplace_unique на внутреннем дереве, который пересылает аргументы to _M_create_node, который переводит аргументы allocator_traits<_Node_allocator>::construct, который пересылает аргументы _S_construct, который переводит аргументы в __a.construct, который с помощью распределителя по умолчанию равен std::allocator<std::pair<const _Key, _Tp> >::construct, который является конструктором пары, который вы пытались избежать... все перед проверкой столкновения в _M_emplace_unique.

Можно утверждать, что стандарт неоднозначен в этом отношении, но я бы назвал это нарушением намерений. Опять же, clang v3.4 с libС++ тоже демонстрирует это поведение, равно как и Visual Studio 2012. Так что, если моя стандартная интерпретация верна, это не работает все три основных инструментальных цепочки.

Я думаю, они все решили, что "если и только если" применяется к вставке, а не к вставке и конструкции.

Я разместил вопрос о std-discussion, чтобы спровоцировать улучшение перехода из таблицы 102, чтобы авторитетно ответить на этот вопрос раз и навсегда.