Come inizializzare std :: vector dall'array in stile C?


174

Qual è il modo più economico per inizializzare un std::vectorda un array in stile C?

Esempio: nella seguente classe, ho un vector, ma a causa di restrizioni esterne, i dati verranno passati come array in 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 passare in rassegna gli elementi o chiamare std::copy(). Ci sono metodi migliori?


11
Il nocciolo del problema è che non c'è modo per il vettore di sapere se lo stesso allocatore è stato usato per creare l'array in stile C. Come tale, il vettore deve allocare memoria usando il proprio allocatore. Altrimenti potrebbe semplicemente sostituire l'array sottostante e sostituirlo con l'array.
Vuoto il

Risposte:


233

Non dimenticare che puoi trattare i puntatori come iteratori:

w_.assign(w, w + len);

3
È una qualità del problema di implementazione. Poiché gli iteratori hanno tag che specificano le loro categorie, un'implementazione di assignè certamente gratuita per usarli per ottimizzare; almeno in VC ++, lo fa davvero.
Pavel Minaev,

38
La soluzione rapida potrebbe essere std :: vector <double> w_ (w, w + len);
jamk,

1
Questo copia gli elementi in un archivio appena creato per 'w_'; 'w_.data' non punterà a 'w'. Devi ancora deallocare 'w'. Non è
previsto

1
Se è un elemento oltre la fine, dovrebbe essere a posto (così come v.end()un iteratore indica uno oltre la fine con il vettore in un caso simile). Se ricevi un'affermazione, allora qualcosa è fuori altrove.
Pavel Minaev,

1
In breve tempo, questo deallocherà la memoria dell'array quando il vettore non rientra nell'ambito?
Adrian Koch,

41

Usa la parola inizializza, quindi non è chiaro se si tratta di un compito una tantum o può accadere più volte.

Se hai solo bisogno di un'inizializzazione singola, puoi inserirla nel costruttore e utilizzare il costruttore del vettore di due iteratori:

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

Altrimenti usa assegnare come precedentemente suggerito:

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

1
Nel mio caso, l'incarico avverrà ripetutamente.
Frank,

12

Puoi "apprendere" automaticamente le dimensioni dell'array:

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

Eventualmente, puoi cambiare l'interfaccia in set_data come sopra. Accetta ancora un array in stile C come primo argomento. Capita solo di prenderlo per riferimento.


Come funziona

[Aggiornamento: vedi qui per una discussione più completa sull'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 funzione, invece decadrà in un puntatore e si perderanno le dimensioni. Ma in C ++, puoi passare un riferimento all'array.

Il passaggio di un array per riferimento richiede che i tipi corrispondano esattamente. La dimensione di un array fa parte del suo tipo. Ciò significa che possiamo usare il parametro modello N per imparare le dimensioni per noi.

Potrebbe essere ancora più semplice avere questa funzione che restituisce un vettore. Con le ottimizzazioni del compilatore appropriate in atto, 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 campione, return { begin(source_array), end(source_array) };è anche possibile
MM

12

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

Nel tuo caso:

w_ (array, std::end(array))
  • array ci darà un puntatore all'inizio dell'array (non ne ha preso il nome),
  • std :: end (array) ci porterà un iteratore alla fine dell'array.

1
Cosa include / versione di C ++ questo richiede?
Vlad,

1
Questo è uno dei costruttori di std :: vector da almeno c ++ 98 in poi .... Si chiama 'range constructor'. cplusplus.com/reference/vector/vector/vector Provalo.
Mugurel,

1
La versione più indipendente è: w_ (std :: begin (array), std :: end (array)); (In futuro è possibile modificare un array C per un contenitore C ++).
Andrew Romanov,

13
Intendiamoci, questo funziona solo se si dispone di un arrayarray reale (che di solito significa che si sta copiando da un array globale o locale (dichiarato nella funzione corrente)). Nel caso del PO, sta ricevendo un puntatore e una lunghezza e, poiché non è modellato sulla lunghezza, non possono passare a ricevere un puntatore a un array di dimensioni o altro, quindi std::endnon funzionerà.
ShadowRanger il

2
vectornon sovraccarica operator(), quindi questo non verrà compilato. std::endessere chiamato su un puntatore non serve neanche a niente (la domanda chiede di assegnare un vettore da un puntatore e una variabile di lunghezza separata). Migliorerebbe la tua risposta per mostrare più contesto su ciò che stai cercando di suggerire
MM

2

La rapida risposta generica:

std::vector<double> vec(carray,carray+carray_size); 

o domanda specifica:

std::vector<double> w_(w,w+len); 

in base a quanto sopra : non dimenticare che puoi considerare i puntatori come iteratori


0

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

Temo non ci sia modo più veloce per implementare (ri) inizializzare il tuo std::vector.


cosa succede se i dati devono essere condivisi tra il vettore e un array? Dobbiamo copiare qualcosa in questo caso?
Vlad,

è una risposta o una domanda? cosa porta alle risposte già esistenti?
Jean-François Fabre

@ Jean-FrançoisFabre e cosa porta il tuo commento? ;) vero, è una risposta scadente data anni fa.
Janusz Lenar,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.