2013-02-16 3 views
10

Посмотрите на следующий код:станд :: карта <> :: вставка с использованием не Copyable объектов и единообразного инициализацию

#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 
} 

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

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

ответ

17

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

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, находит соответствующий mova ­, а результат представляет собой пару rvalue, которая связывается с P&& - перегрузкой функции insert.

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

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

+1

Да, это в основном то, что я написал перед удалением моего ответа. У меня есть сомнения: почему здесь создан 'initializer_list <>'? 'std :: pair ' похоже, что у него нет конструктора. Я думал, что единообразный синтаксис инициализации просто выберет регулярный конструктор 'pair <>'. –

+1

Кроме того, элементы initializer_list <> должны быть одного типа, нет? – eladidan

+0

+1 для обоих комментариев. В моем примере даже не должно быть 'initializer_list'. Это больше похоже на вызов конструктора std :: pair, использующий единообразную инициализацию. – mfontanini