2012-04-18 18 views
11

Sto cercando di inserire una copia di un elemento vector esistente per raddoppiarlo. Il seguente codice ha funzionato nelle versioni precedenti, ma non riesce in Visual Studio 2010.Come inserire un elemento duplicato in un vettore?

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), test[0]); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 

uscita è -17891602 1 2, previsto 1 1 2.

Ho capito perché sta accadendo: il vettore viene riallocato e il riferimento diventa non valido prima di essere copiato nel punto di inserimento. Il Visual Studio precedente apparentemente faceva le cose in un ordine diverso, dimostrando così che un possibile risultato di comportamento indefinito è quello di funzionare correttamente e anche dimostrare che non è mai qualcosa su cui dovresti fare affidamento.

Ho trovato due modi diversi per risolvere questo problema. Uno è quello di utilizzare reserve per assicurarsi che nessun riassegnazione avviene:

test.reserve(test.size() + 1); 
    test.insert(test.begin(), test[0]); 

L'altro è quello di fare una copia del riferimento in modo che non c'è dipendenza dal riferimento rimangono validi:

template<typename T> 
T make_copy(const T & original) 
{ 
    return original; 
} 

    test.insert(test.begin(), make_copy(test[0])); 

Anche se entrambi funzionano, nessuno dei due sembra una soluzione naturale. C'è qualcosa che mi manca?

+0

BTW vc11 dev preview fornisce '1 1 2' per il primo esempio. –

+0

@Jesse, questo non mi sorprende. È stato selezionato il sovraccarico Rvalue di 'insert' che sembra un bug che potrebbero aver risolto. Il codice è completamente diverso tra quel sovraccarico e quello che prende un riferimento const. –

+0

Il casting su int funziona? –

risposta

1

Credo che questo sia un comportamento definito. In §23.2.3 dello standard C++ 2011, la tabella 100 elenca i requisiti del contenitore di sequenza e per questo caso è presente una voce. Esso fornisce l'esempio espressione

a.insert(p,t) 

dove a è un valore di X che è un tipo di contenitore sequenza contenente elementi di tipo T, p è un iteratore const a, e t è un lvalue o rvalue const di tipo X::value_type , ovvero T.

L'affermazione di questa espressione è:

Richiede:T sarà CopyInsertable in X. Per vector e deque, T deve essere anche CopyAssignable.
Effetti: Inserisce una copia di t prima dello p.

L'unica citazione specifica rilevante vettore ho potuto trovare è nella §23.3.6.5 paragrafo 1:

Osservazioni: cause riallocazione se la nuova dimensione è superiore alla vecchia capienza. Se non avviene alcuna riallocazione, tutti gli iteratori e i riferimenti prima del punto di inserimento rimangono validi.

Anche se questo fa menzione del riallineamento del vettore, non fa un'eccezione ai precedenti requisiti per insert sui contenitori di sequenza.

Come per aggirare questo problema, sono d'accordo con il suggerimento di @ EdChum di fare solo una copia dell'elemento e inserire quella copia.

+0

Non vedo nulla nella descrizione che chiami il caso in cui' t' è un riferimento a un membro di 'a'. –

4

Il problema è che vector::insert prende un riferimento a un valore come secondo parametro e non a un valore. Non è necessario il modello per fare una copia, basta usare un costruttore di copie per creare un altro oggetto, che sarà passato per riferimento. Questa copia rimane valida anche se il vettore viene ridimensionato.

#include <iostream> 
#include <vector> 

using namespace std; 

int main(int argc, char* argv[]) 
{ 
    vector<int> test; 
    test.push_back(1); 
    test.push_back(2); 
    test.insert(test.begin(), int(test[0])); 
    cout << test[0] << " " << test[1] << " " << test[2] << endl; 
    return 0; 
} 
+0

Questo è solo un caso di test. Il mio codice attuale contiene elementi molto più complicati di un int e il costruttore di un temporaneo diventa solo brutto. Buon suggerimento però –

Problemi correlati