2010-03-12 11 views
124

Qual è il modo più economico per inizializzare uno std::vector da un array in stile C?Come inizializzare std :: vector dall'array in stile C?

Esempio: Nel seguente classe, ho un vector, ma a causa delle restrizioni esterne, i dati verranno trasmessi come matrice stile C:

class Foo { 
    std::vector<double> w_; 
public: 
    void set_data(double* w, int len){ 
    // how to cheaply initialize the std::vector? 
} 

Ovviamente, posso chiamare w_.resize() e poi cappio sopra gli elementi, o chiamare std::copy(). Ci sono metodi migliori?

+7

Il nocciolo del problema è che non è possibile per il vettore sapere se lo stesso allocatore è stato utilizzato per creare l'array in stile C. In quanto tale, il vettore deve allocare la memoria utilizzando il proprio allocatore. Altrimenti potrebbe semplicemente sostituire l'array sottostante e sostituirlo con l'array. – Void

risposta

180

Non dimenticate che è possibile trattare i puntatori come iteratori:

w_.assign(w, w + len); 
+0

Esiste una differenza di prestazioni tra 'std :: copy (w, w + len, w_.begin())' e la soluzione 'assign'? – Frank

+0

Oh, immagino che la differenza sia che 'std :: copy' non ridimensionerà la matrice. – Frank

+0

Non so se * assign * sia abbastanza intelligente da calcolare quanto * w * sia lontano da * w + len * (un iteratore generico AFAIK non fornisce un modo rapido per farlo), quindi potresti migliorare un po ' le esibizioni che mettono un w_.reserve (len) prima di quella dichiarazione; nel peggiore dei casi non ottieni nulla. In questo modo dovrebbe avere più o meno le prestazioni di ridimensionamento + copia. –

31

si utilizza la parola inizializzare quindi non è chiaro se questo è l'assegnazione di una volta o può accadere più volte.

Se avete solo bisogno di un'inizializzazione di una volta, si può mettere nel costruttore e utilizzare i due iteratore vettore costruttore:

Foo::Foo(double* w, int len) : w_(w, w + len) { } 

Altrimenti utilizzare assegnare come suggerito in precedenza:

void set_data(double* w, int len) 
{ 
    w_.assign(w, w + len); 
} 
+0

Nel mio caso, l'assegnazione avverrà ripetutamente. – Frank

1

std::vector<double>::assign è la strada da percorrere, perché è il piccolo codice. Ma come funziona, in realtà? Non si ridimensiona e quindi copia? Nell'implementazione di MS di STL che sto usando, fa esattamente così.

Ho paura che non ci sia il modo più veloce per implementare (ri) l'inizializzazione del tuo std::vector.

+0

e se i dati da condividere tra il vettore e un array? Abbiamo bisogno di copiare qualcosa in questo caso? – Vlad

8

Si puo 'imparare' la dimensione dell'array automaticamente:

template<typename T, size_t N> 
void set_data(const T (&w)[N]){ 
    w_.assign(w, w+N); 
} 

Eventualmente, è possibile modificare l'interfaccia per set_data come sopra. Accetta ancora un array in stile C come primo argomento. Capita semplicemente di prenderlo per riferimento.


Come funziona

[Update: Vedere here per una discussione più completa per l'apprendimento delle dimensioni]

Ecco una soluzione più generale:

template<typename T, size_t N> 
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) { 
    target_vector.assign(source_array, source_array+N); 
} 

Questo funziona perché l'array viene passato come riferimento a un array. In C/C++, non è possibile passare una matrice come una funzione, ma decadrà in un puntatore e si perde la dimensione. Ma in C++, è possibile passare un riferimento alla matrice.

Il passaggio di un array per riferimento richiede che i tipi corrispondano esattamente. La dimensione di un array è parte del suo tipo. Ciò significa che possiamo utilizzare il parametro del modello N per apprendere la dimensione per noi.

Potrebbe essere ancora più semplice avere questa funzione che restituisce un vettore. Con le opportune ottimizzazioni del compilatore in vigore, questo dovrebbe essere più veloce di quanto sembri.

template<typename T, size_t N> 
vector<T> convert_array_to_vector(const T (&source_array)[N]) { 
    return vector<T>(source_array, source_array+N); 
} 
+1

Nell'ultimo esempio, 'return {begin (source_array), end (source_array)};' è anche possibile –

10

Beh, Pavel era vicino, ma c'è anche una soluzione più semplice ed elegante per inizializzare un contenitore sequenziale da un array c stile.

Nel tuo caso:

w_ (array, std::end(array)) 
  • serie ci ottenere un puntatore all'inizio della matrice (non cogliere il suo nome),
  • std :: end (array) otterrà noi un iteratore alla fine dell'array.
+1

Cosa include/versione di C++ richiede? – Vlad

+1

Questo è uno dei costruttori di std :: vector da almeno C++ 98 in poi .... Si chiama 'costruttore di intervalli'. http://www.cplusplus.com/reference/vector/vector/vector/ Provalo. – Mugurel

+1

Più versione indipendente è: w_ (std :: begin (array), std :: end (array)); (In futuro è possibile modificare un array C per un contenitore C++). –

Problemi correlati