2012-05-23 23 views
41

Considérese el siguiente programa que inserta una serie de elementos en un vector:¿Hay una forma estándar de mover un rango en un vector?

vector<string> v1; 
vector<string> v2; 

v1.push_back("one"); 
v1.push_back("two"); 
v1.push_back("three"); 

v2.push_back("four"); 
v2.push_back("five"); 
v2.push_back("six"); 

v1.insert(v1.end(), v2.begin(), v2.end()); 

Este eficientemente copias de la gama, la asignación de espacio suficiente en el vector objetivo para toda la gama de modo que se requiere un máximo de un cambio de tamaño . Ahora considere el siguiente programa que intenta mover un rango en un vector:

vector<string> v1; 
vector<string> v2; 

v1.push_back("one"); 
v1.push_back("two"); 
v1.push_back("three"); 

v2.push_back("four"); 
v2.push_back("five"); 
v2.push_back("six"); 

for_each (v2.begin(), v2.end(), [&v1](string & s) 
{ 
    v1.emplace_back(std::move(s)); 
}); 

Esto lleva a cabo un movimiento acertado, pero no disfrutar de los beneficios que se insertan() tiene con respecto a preasignar espacio en el vector objetivo, por lo el vector podría cambiar de tamaño varias veces durante la operación.

Entonces mi pregunta es, ¿existe un inserto equivalente que pueda mover un rango en un vector?

+1

Si necesita preasignar espacio en el vector, use 'std :: vector :: reserve', y mantenga' push_back'/'emplace_back'. – rubenvb

+0

@rubenvb Sí, pensé que esa sería probablemente la respuesta, es una lástima que no haya un método tan limpio como 'insert()'. – Benj

+0

Eso sería una optimización opcional, solo posible cuando el rango está definido por iteradores de acceso aleatorio. No cuentes con eso –

Respuesta

65

utiliza un move_iterator con insert:

v1.insert(v1.end(), make_move_iterator(v2.begin()), make_move_iterator(v2.end())); 

El ejemplo de 24.5.3 es casi exactamente esto.

Obtendrá la optimización que desee si (a) vector::insert utiliza el despacho de etiqueta de iterador para detectar el iterador de acceso aleatorio y precalcular el tamaño (que supone que hace en su ejemplo que copia), y (b) move_iterator preserva la categoría de iterador del iterador que envuelve (que es requerido por el estándar).

En un punto oscuro: estoy bastante seguro de que vector::insert puede ubicarse desde la fuente (que es irrelevante aquí, ya que la fuente es del mismo tipo que el destino, por lo que una ubicación es lo mismo que una copia/movimiento, pero sería relevante para ejemplos idénticos). Todavía no he encontrado una declaración de que sea obligatorio hacerlo, lo deduje del hecho de que el requisito del par de iteradores i,j pasado a insert es TEmplaceConstructible de *i.

+1

Excelente, no sabía nada sobre 'make_move_iterator' – Benj

+1

+1 para el fondo – sehe

+4

Hmm, he descubierto que también es posible usar' make_move_iterator' para convertir 'std :: copy_if' en el equivalente de' std :: move_if'. Eso es muy útil. – Benj

32
  1. std::move algoritmo con preasignación:

    #include <iterator> 
    #include <algorithm> 
    
    v1.reserve(v1.size() + v2.size()); // optional 
    std::move(v2.begin(), v2.end(), std::back_inserter(v1)); 
    
  2. los siguientes sería más flexible aún:

    v1.insert(v1.end(), 
        std::make_move_iterator(v2.begin()), 
        std::make_move_iterator(v2.end())); 
    

    Steve Jessop proporciona información de antecedentes sobre, precisamente, lo que hace y, probablemente, cómo lo hace asi que.

+0

Las posibilidades de cambiar el tamaño del 'vector' de destino una sola vez son muy escasas. –

+1

Oh ordenado, no sabía acerca de esta forma de 'std :: move'.Aunque supongo que el 'back_inserter' aún podría causar múltiples cambios de tamaño. – Benj

+0

@BenVoigt ¿Por qué? Supongo que depende de la implementación. vector :: reserve es tu amigo. Déjame revisar el estándar. – sehe

Cuestiones relacionadas