std :: back_inserter para un std :: set?


94

Supongo que esta es una pregunta simple. Necesito hacer algo como esto:

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

Por supuesto, std::back_inserterno funciona ya que no hay push_back. std::insertertambién necesita un iterador? No lo he usado, std::inserterasí que no estoy seguro de qué hacer.

¿Alguien tiene alguna idea?


Por supuesto, mi otra opción es usar un vector para s2y luego ordenarlo más tarde. ¿Quizás eso sea mejor?

Respuestas:


139

setno tiene push_backporque la posición de un elemento está determinada por el comparador del conjunto. Úselo std::insertery páselo .begin():

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

El iterador de inserción llamará s2.insert(s2.begin(), x)donde xes el valor pasado al iterador cuando se escribe en él. El conjunto usa el iterador como una pista de dónde insertar. También podrías usar s2.end().


2
Dado que también inserter(vec, vec.end())funciona para vectores, ¿por qué alguien usa back_inserter en primer lugar?
NHDaly

7
@NHDaly: porque back_inserter es más rápido
marton78

@ marton78 ¿Pero no debería ser más rápido por un margen muy pequeño, si es que lo hace? Llamar en insertlugar de push_backen un vector debe ser aproximadamente idéntico (O (1)) cuando no es necesario mover ningún elemento.
Felix Dombek

3
@FelixDombek tienes razón, no será mucho más lento. v.insert(x, v.end())tendrá una rama adicional al principio (debido a que mueve n elementos, pero aquí n es cero). Sin embargo, usar inserter1) comunica una intención diferente a usar push_back2) es inusual y hace que el lector se detenga y piense 3) es una pesimización prematura.
marton78

0

En 2016 hubo una propuesta para tener un " inserteriterador de argumento único ". https://isocpp.org/files/papers/p0471r0.html . No pude encontrar si la propuesta avanzaba. Creo que tiene sentido.

Por ahora puede tener este comportamiento definiendo la función de creador:

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

Usado como:

std::transform(begin(my_vec), end(my_vec), sinserter(my_set), [](auto& e){return e.member;});

¿Está destinado a funcionar en todos los contenedores estándar? No funciona en std :: forward_list (el error del compilador es "forward_list no tiene un miembro llamado 'insertar'", dentro de la instanciación de insert_iterator::operator=). ¿Deberia?
Don Hatch

@DonHatch, cualquier cosa que tenga insert(y end). parece que forward_listno tiene una insertoperación en primer lugar, solo insert_after. E incluso si eso se cambia, no se puede insertar después del final, creo. ¿No puedes usar un std::listen su lugar?
alfC

Seguro, personalmente no necesito std :: forward_list. Pero estoy interesado en un "¿cómo copio un contenedor a otro?" Genérico. para todos los pares de contenedores para los que tiene sentido. Mi interés actual es ejercitar asignadores de contenedores genéricos como short_alloc de @HowardHinnant.
Don Hatch

@DonHatch, es que hay muchas variantes a esa pregunta. ("" ¿Cómo copio un contenedor a otro? "). Por ejemplo, ¿desea conservar los valores originales, desea minimizar las asignaciones, etc. Para el caso más simple, la mejor respuesta en mi opinión es usar el constructor el contenedor que toma dos iteradores (la mayoría de los contenedores pueden tomar eso) NewContaner new_container(old_other_container.begin(), old_other_container.end()).
alfC

1
Sí, std :: set no es un SequentialContainer, y está bien :-) existing_list = std::list(c.begin(), c.end(), existing_list.get_allocator()) Muy bien, creo que esa es mi respuesta. ¡Salud!
Don Hatch
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.