std :: back_inserter pour un std :: set?

94

Je suppose que c'est une question simple. J'ai besoin de faire quelque chose comme ça:

std::set<int> s1, s2;
s1 = getAnExcitingSet();
std::transform(s1.begin(), s1.end(), std::back_inserter(s2), ExcitingUnaryFunctor());

Bien sûr, std::back_inserterne fonctionne pas car il n'y a pas push_back. std::insertera également besoin d'un itérateur? Je ne l'ai pas utilisé, std::inserterdonc je ne sais pas quoi faire.

est-ce que quelqu'un a une idée?


Bien sûr, mon autre option est d'utiliser un vecteur pour s2, puis de le trier plus tard. C'est peut-être mieux?

rlbond
la source

Réponses:

139

setn'a pas push_backcar la position d'un élément est déterminée par le comparateur de l'ensemble. Utilisez std::inserteret passez-le .begin():

std::set<int> s1, s2;
s1 = getAnExcitingSet();
transform(s1.begin(), s1.end(), 
          std::inserter(s2, s2.begin()), ExcitingUnaryFunctor());

L'itérateur d'insertion appellera alors s2.insert(s2.begin(), x)xest la valeur transmise à l'itérateur lorsqu'il y est écrit. L'ensemble utilise l'itérateur pour indiquer où insérer. Vous pouvez également utiliser s2.end().

Johannes Schaub - litb
la source
2
Puisque inserter(vec, vec.end())fonctionne aussi pour les vecteurs, pourquoi est-ce que quelqu'un utilise back_inserter en premier lieu?
NHDaly
7
@NHDaly: parce que back_inserter est plus rapide
marton78
@ marton78 Mais ne devrait-il pas être plus rapide que par une très petite marge, voire pas du tout? L'appel insertau lieu de push_backsur un vecteur doit être à peu près identique (O (1)) quand aucun élément ne doit être déplacé.
Felix Dombek
3
@FelixDombek vous avez raison, ce ne sera pas beaucoup plus lent. v.insert(x, v.end())aura une branche supplémentaire au début (en raison du déplacement de n éléments, mais ici n vaut zéro). Cependant, utiliser inserter1) communique une intention différente de l'utilisation de push_back2) est inhabituel et incite le lecteur à s'arrêter et à penser que 3) est une pessimisation prématurée.
marton78
0

En 2016, il a été proposé d'avoir un « inserteritérateur d' argument unique ». https://isocpp.org/files/papers/p0471r0.html . Je n'ai pas pu trouver si c'était la proposition avancée. Je pense que cela a du sens.

Pour l'instant, vous pouvez avoir ce comportement définissant la fonction maker:

template<class Container>
auto sinserter(Container& c){
    using std::end;
    return std::inserter(c, end(c));
}

Utilisé comme:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});
alfC
la source
Est-ce destiné à fonctionner sur tous les conteneurs standard? Cela ne fonctionne pas sur std :: forward_list (l'erreur du compilateur est "forward_list n'a pas de membre nommé 'insert'", à l'intérieur de l'instanciation de insert_iterator::operator=). Devrait-il?
Don Hatch le
@DonHatch, tout ce qui a insert(et end). il semble qu'il forward_listn'y ait pas d' insertopération en premier lieu, seulement insert_after. Et même si cela est changé, il ne peut pas être inséré après la fin, je pense. Vous ne pouvez pas utiliser un à la std::listplace?
alfC
Bien sûr, je n'ai personnellement pas besoin de std :: forward_list. Mais je suis intéressé par un générique "comment copier un conteneur dans un autre?" pour toutes les paires de conteneurs pour lesquelles cela a du sens. Mon intérêt actuel est d 'exercer des allocateurs de conteneurs génériques tels que short_alloc de @HowardHinnant.
Don Hatch le
@DonHatch, le fait est qu'il existe de nombreuses variantes à cette question. ("" comment copier un conteneur dans un autre? "). Par exemple, voulez-vous conserver les valeurs d'origine, voulez-vous minimiser les allocations, etc. Pour le cas le plus simple, la meilleure réponse à mon avis est d'utiliser le constructeur le conteneur qui prend deux itérateurs (la plupart des conteneurs peuvent accepter cela) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC
1
Oui, std :: set n'est pas un SequentialContainer, et c'est existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) très bien :-) Très bien, je pense que c'est ma réponse. À votre santé!
Don Hatch le