std :: map & lt; & gt; :: insert с использованием не копируемых объектов и равномерной инициализации

Посмотрите на следующий код:

#include <utility>
#include <map>

// non-copyable but movable
struct non_copyable {
non_copyable() = default;

non_copyable(non_copyable&&) = default;
non_copyable& operator=(non_copyable&&) = default;

// you shall not copy
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};

int main() {
std::map<int, non_copyable> map;
//map.insert({ 1, non_copyable() });  < FAILS
map.insert(std::make_pair(1, non_copyable()));
// ^ same and works
}

Компиляция этого фрагмента завершается неудачно при раскомментировании отмеченной строки в g ++ 4.7. Произошедшая ошибка указывает на то, что non_copyable не может быть скопировано, но я ожидал, что это будет перемещено.

Почему вставка std::pair построено с использованием одинаковой ошибки инициализации, но не с использованием std::make_pair? Разве оба не должны давать значения, которые могут быть успешно перемещены на карту?

11

Решение

[Это полное переписывание. Мой предыдущий ответ не имел ничего общего с проблемой.]

map имеет два соответствующих insert перегрузки:

  • insert(const value_type& value), а также

  • <template typename P> insert(P&& value),

Когда вы используете простой список-инициализатор map.insert({1, non_copyable()});, все возможные перегрузки рассматриваются. Но только первый (тот, который принимает const value_type&), поскольку другой не имеет смысла (нет никакого способа волшебным образом догадаться, что вы хотели создать пару). Первая перегрузка, конечно, не работает, так как ваш элемент не копируется.

Вы можете заставить работать вторую перегрузку, явно создав пару, либо make_pair, как вы уже описали, или явно указав тип значения:

typedef std::map<int, non_copyable> map_type;

map_type m;
m.insert(map_type::value_type({1, non_copyable()}));

Теперь инициализатор списка знает, что нужно искать map_type::value_type конструкторы, находит соответствующий подвижный, и в результате получается пара значений, которая связывается с P&&перегрузка insert функция.

(Другой вариант заключается в использовании emplace() с piecewise_construct а также forward_as_tupleхотя это было бы намного более многословным.)

Я полагаю, что мораль здесь в том, что инициализаторы списков ищут жизнеспособные перегрузки — но они должны знать, что искать!

18

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

Других решений пока нет …

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