2016-10-01 2 views
3

Рассмотрим в качестве примера std::unordered_set из std::unique_ptr<T>. Могу ли я переместить элемент в другом месте?Как получить тип перемещения только из контейнера STL?

#include <unordered_set> 
#include <iostream> 
#include <memory> 
#include <vector> 

int main() 
{ 
    std::unordered_set<std::unique_ptr<int>> mySet; 

    mySet.insert(std::make_unique<int>(1)); 
    mySet.insert(std::make_unique<int>(2)); 
    mySet.insert(std::make_unique<int>(3)); 

    std::vector<std::unique_ptr<int>> myVector; 

    for (auto&& element : mySet) 
    { 
     std::cout << *element << std::endl; 
     //myVector.push_back(element); won't compile as you can only get a const ref to the key 
    } 
} 

У меня есть очень практический пример кода, где я хотел бы сделать это, но я уменьшенный использовать std::shared_ptr. Вы знаете другую (лучше?) Альтернативу?

+1

Обратите внимание, что эта проблема не возникает, если 'mySet' были' станд :: вектор' вместо этого; он свойствен [контейнерам на узлах] (http://en.cppreference.com/w/cpp/container/node_handle). Также обратите внимание, что если вам удастся его перенести, он может сделать другие операции над этим заданием неопределенным поведением, так как перемещенный объект перестает быть действительным для чтения, например, для вставки или нахождения, поэтому следует проявлять осторожность при использовании контейнера вообще. – metal

+0

@metal Я, вероятно, что-то делаю неправильно, но у меня такая же ошибка компиляции только с векторами, см. Http://coliru.stacked-crooked.com/a/51af3ac220c44619. edit: Мой плохой, работает с 'std :: move'. Благодаря ! Новый код: http://coliru.stacked-crooked.com/a/95cb8a827d587723 – matovitch

ответ

8

В C++ 03, C++ 11 и C++ 14, а не напрямую. Вы должны были бы изменить тип, чтобы быть что-то вроде:

template <class T> 
struct handle { 
    mutable std::unique_ptr<T> owning_ptr; 
    T* observing_ptr; // enforce that observing_ptr == owning_ptr.get() on construction 

    // define operator<, hash, etc. in terms of the observing ptr 
}; 

С этим, вы можете написать:

std::unordered_set<handle<int>> mySet; 
// initialize as appropriate 

for (auto& elem : mySet) { 
    myVector.push_back(std::move(elem.owning_ptr));   
} 
mySet.clear(); 

Это все еще будет хорошо определено поведение, потому что мы не возиться с какой-либо внутренних элементов контейнера - указатель наблюдения будет по-прежнему действителен в конце clear(), сейчас myVector принадлежит ему.


В C++ 17, мы можем сделать это непосредственно и более просто с помощью extract():

for (auto it = mySet.begin(); it != mySet.end(); 
{ 
    std::cout << **it << std::endl; 
    myVector.push_back(std::move(
     mySet.extract(it++).value())); 
} 
+0

Вы можете сделать контейнер содержащим mutable tagged union-like от умного указателя и указателя немого, который согласен на упорядочение, и операции, которая свопирует от умного до немого, возвращая умный. Экстракт умный, стереть немой, а боб - твой дядя. – Yakk

+0

@Yakk На шаг впереди вас :) За исключением того, что это не союз, но да, это может быть. – Barry

+1

Должно ли это быть 'handle ', а не 'handle >'? – ArchbishopOfBanterbury

Смежные вопросы