2012-05-23 8 views
41

consideri il seguente programma che inserisce un intervallo di elementi in un vettore:Esiste un modo standard per spostare un intervallo in un vettore?

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()); 

Questa efficientemente copia il raggio, prevedere spazi sufficienti nel vettore di destinazione per l'intero intervallo in modo che sarà richiesto un massimo di un ridimensionamento . Consideriamo ora il seguente programma che tenta di spostare una serie in un vettore:

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)); 
}); 

Questo esegue una mossa di successo, ma non godere dei benefici che inseriscono() ha per quanto riguarda preallocare spazio nel vettore di destinazione, in modo da il vettore potrebbe essere ridimensionato più volte durante l'operazione.

Quindi la mia domanda è: esiste un equivalente di inserimento che può spostare un intervallo in un vettore?

+1

Se è necessario preallocare lo spazio nel vettore, utilizzare 'std :: vector :: reserve' e mantenere' push_back'/'emplace_back'. – rubenvb

+0

@rubenvb Sì, ho pensato che probabilmente sarebbe stata la risposta, è solo un peccato che non ci sia un metodo pulito come 'insert()' è. – Benj

+0

Questa sarebbe un'ottimizzazione opzionale, possibile solo quando l'intervallo è definito da iteratori ad accesso casuale. Non contarci. –

risposta

65

si utilizza un move_iterator con insert:

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

L'esempio in 24.5.3 è quasi esattamente questo.

si otterrà l'ottimizzazione che vuoi, se (a) vector::insert utilizza iteratore-tag spedizione per rilevare l'iteratore ad accesso casuale e precalculate le dimensioni (che hai pensato che fa nel tuo esempio che copia), e (b) move_iterator conserva la categoria iteratore dell'iteratore che esegue il wrapping (che è richiesto dallo standard).

Su un punto oscuro: Sono abbastanza sicuro che vector::insert può colloco dalla sorgente (che è irrilevante, dal momento che la fonte è lo stesso tipo come destinazione, quindi un colloco è lo stesso di una copia/spostamento, ma sarebbe rilevante per esempi altrimenti identici). Non ho ancora trovato una dichiarazione che è necessario farlo, l'ho appena dedotto dal fatto che il requisito sulla coppia iteratore i,j passato a insert è che T è EmplaceConstructible da *i.

+1

Eccellente, non sapeva di 'make_move_iterator' – Benj

+1

+1 per lo sfondo – sehe

+4

Hmm, ho scoperto che è anche possibile usare' make_move_iterator' per trasformare 'std :: copy_if' nell'equivalente di' std :: move_if'. È molto utile. – Benj

32
  1. std::move algoritmo con preallocazione:

    #include <iterator> 
    #include <algorithm> 
    
    v1.reserve(v1.size() + v2.size()); // optional 
    std::move(v2.begin(), v2.end(), std::back_inserter(v1)); 
    
  2. Quello che segue sarebbe ancora più flessibile:

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

    Steve Jessop fornito informazioni di base su esattamente ciò che fa e, probabilmente, come lo fa così.

+0

Le possibilità di ridimensionare il vettore '' di destinazione una sola volta sono molto ridotte. –

+1

Oh bene, non sapevo su questa forma di 'std :: move'.Anche se penso che il 'back_inserter' potrebbe comunque causare ridimensionamenti multipli. – Benj

+0

@ BenVoigt Perché? Suppongo che dipenda dall'implementazione. vector :: reserve è tuo amico. Fammi controllare lo standard. – sehe

Problemi correlati