2017-07-11 3 views
1

J'ai un std::set<std::unique_ptr<T>> et je voudrais passer à un std::vector<std::unique_ptr<T>>déplacer seul type extraction de std :: set

#include <set> 
#include <vector> 
#include <memory> 

class C {}; 

int main() 
{ 
    std::set<std::unique_ptr<const C>> s; 
    std::vector<std::unique_ptr<const C>> v; 
    std::move(s.begin(), s.end(), std::back_inserter(v)); 
} 

Cela donne l'erreur suivante sur VS2017:

erreur C2280 : 'std :: unique_ptr> :: unique_ptr (const std :: unique_ptr < _Ty, std :: default_delete < _Ty >> &)': la tentative de faire référence à une fonction supprimée

Ne pouvons-nous pas déplacer les itérateurs vers des variables non-const à partir d'un std::set? Quelle pourrait être une solution viable à ce problème?

Répondre

4

Pour extraire uniquement déplacer des éléments d'un ensemble, la seule possibilité est d'utiliser la méthode extract, qui a été ajouté dans 17 C++:

while (!s.empty()) 
    v.emplace_back(std::move(s.extract(s.begin()).value()); 

Si vous ne pouvez pas utiliser 17 C++ vous pouvez modifier un élément d'un ensemble (par exemple en utilisant mutable) uniquement si vous vous assurez qu'il conserve la même position dans la commande imposée, c'est-à-dire tant qu'il a le même résultat sous votre comparateur par rapport à tous les autres membres de l'ensemble. Vous pouvez le faire en fournissant un comparateur qui commande vides pointeurs uniques avant non vide (notez que la norme ne garantit pas) et l'effacement de l'élément modifié immédiatement après l'avoir modifié:

template<class T> struct MutableWrapper { mutable T value; }; 
template<class T> struct MutableWrapperCompare { 
    bool operator()(MutableWrapper<T> const& lhs, MutableWrapper<T> const& rhs) { 
    return lhs.value && rhs.value ? lhs.value < rhs.value : rhs.value; 
    } 
}; 

int main() 
{ 
    std::set<MutableWrapper<std::unique_ptr<const C>>, MutableWrapperCompare<std::unique_ptr<const C>>> s; 
    std::vector<std::unique_ptr<const C>> v; 
    while (!s.empty()) 
    { 
    v.emplace_back(std::move(s.begin()->value)); 
    s.erase(s.begin()); 
    } 
} 

Ceci est cependant assez laid et dangereux; vous seriez mieux d'utiliser boost::container::set de Boost.Container, qui a la méthode d'extraction 17 C++ (since 1.62.0, il was undocumented, mais cela est juste un oubli, notez les extract méthodes correspondantes sont documentées pour map et multimap).

+0

Y at-il de toute façon que je puisse implémenter cette méthode pour ma base source jusqu'à ce que le compilateur que j'utilise l'implémente? – tunc

+1

@tunc sorte de, mais c'est moche - voir ci-dessus. Si possible, je recommande d'utiliser Boost (boost :: container :: set, v 1.62.0 et plus). – ecatmur