Bagaimana cara menginisialisasi std :: vector dari C-style array?

174

Apa cara termurah untuk menginisialisasi std::vectordari array C-style?

Contoh: Di kelas berikut, saya punya vector, tetapi karena batasan luar, data akan diteruskan sebagai larik gaya-C:

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

Jelas, saya bisa menelepon w_.resize()dan kemudian mengulang elemen, atau menelepon std::copy(). Apakah ada metode yang lebih baik?

jujur
sumber
11
Inti masalahnya adalah tidak ada cara bagi vektor untuk mengetahui apakah pengalokasi yang sama digunakan untuk membuat larik gaya-C Anda. Karena itu vektor harus mengalokasikan memori menggunakan pengalokasi sendiri. Kalau tidak, itu hanya bisa menukar array yang mendasarinya dan menggantinya dengan array Anda.
Void

Jawaban:

233

Jangan lupa bahwa Anda dapat memperlakukan pointer sebagai iterator:

w_.assign(w, w + len);
Pavel Minaev
sumber
3
Ini masalah kualitas implementasi. Karena iterator memiliki tag yang menentukan kategorinya, penerapannya assigntentu gratis untuk menggunakannya; setidaknya di VC ++, memang melakukan hal itu.
Pavel Minaev
38
Solusi cepatnya bisa std :: vector <double> w_ (w, w + len);
jamk
1
Ini menyalin elemen ke penyimpanan yang baru dibuat untuk 'w_'; 'w_.data' tidak akan menunjuk ke 'w'. Anda masih harus membatalkan alokasi 'w'. Tidak ada transfer kepemilikan
Indy9000
1
Jika salah satu elemen melewati akhir, itu harus baik-baik saja (seperti halnya v.end()iterator menunjuk satu melewati akhir dengan vektor dalam kasus yang sama). Jika Anda mendapatkan penegasan, maka ada sesuatu yang salah di tempat lain.
Pavel Minaev
1
Dengan cepat, apakah ini akan membatalkan alokasi memori array ketika vektor keluar dari ruang lingkup?
Adrian Koch
41

Anda menggunakan kata inisialisasi sehingga tidak jelas apakah ini tugas satu kali atau dapat terjadi beberapa kali.

Jika Anda hanya memerlukan inisialisasi satu kali, Anda dapat meletakkannya di konstruktor dan menggunakan dua konstruktor vektor iterator:

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

Kalau tidak, gunakan penetapan seperti yang disarankan sebelumnya:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}
Markus B
sumber
1
Dalam kasus saya, tugas akan terjadi berulang kali.
Frank
12

Anda dapat 'mempelajari' ukuran array secara otomatis:

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

Semoga Anda dapat mengubah antarmuka ke set_data seperti di atas. Ia masih menerima larik gaya-C sebagai argumen pertamanya. Kebetulan mengambilnya dengan referensi.


Bagaimana itu bekerja

[Pembaruan: Lihat di sini untuk diskusi yang lebih komprehensif tentang mempelajari ukuran]

Ini solusi yang lebih umum:

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

Ini berfungsi karena array dilewatkan sebagai referensi-ke-an-array. Dalam C / C ++, Anda tidak bisa melewatkan array sebagai fungsi, sebaliknya ia akan membusuk ke sebuah pointer dan Anda kehilangan ukurannya. Namun dalam C ++, Anda bisa meneruskan referensi ke array.

Melewati sebuah array dengan referensi membutuhkan tipe yang tepat. Ukuran array adalah bagian dari tipenya. Ini berarti kita dapat menggunakan parameter template N untuk mempelajari ukurannya bagi kita.

Mungkin lebih sederhana untuk memiliki fungsi ini yang mengembalikan vektor. Dengan optimalisasi kompiler yang berlaku, ini harus lebih cepat daripada yang terlihat.

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);
}
Aaron McDaid
sumber
1
Dalam sampel terakhir, return { begin(source_array), end(source_array) };juga dimungkinkan
MM
12

Yah, Pavel sudah dekat, tetapi bahkan ada solusi yang lebih sederhana dan elegan untuk menginisialisasi wadah berurutan dari array gaya ac.

Dalam kasus Anda:

w_ (array, std::end(array))
  • array akan memberi kita pointer ke awal array (tidak menangkap nama itu),
  • std :: end (array) akan membawa kita iterator ke akhir array.
Mugurel
sumber
1
Apa yang termasuk / versi C ++ yang dibutuhkan?
Vlad
1
Ini adalah salah satu konstruktor dari std :: vector dari setidaknya c ++ 98 dan seterusnya .... Ini disebut 'range constructor'. cplusplus.com/reference/vector/vector/vector Cobalah.
Mugurel
1
Versi lebih independen adalah: w_ (std :: begin (array), std :: end (array)); (Di masa depan Anda bisa mengubah array C untuk wadah C ++).
Andrew Romanov
13
Pikiran Anda, ini hanya berfungsi jika Anda memiliki nyata array(yang biasanya berarti Anda menyalin dari array global atau lokal (dideklarasikan dalam fungsi saat ini)). Dalam kasus OP, dia menerima pointer dan panjang, dan karena itu tidak tergambar panjang, mereka tidak dapat berubah untuk menerima pointer ke array berukuran atau apa pun, jadi std::endtidak akan berfungsi.
ShadowRanger
2
vectortidak kelebihan operator(), jadi ini tidak dapat dikompilasi. std::enddipanggil pada pointer juga tidak ada gunanya (pertanyaannya meminta untuk menetapkan vektor dari pointer dan variabel panjang yang terpisah). Ini akan meningkatkan jawaban Anda untuk menunjukkan lebih banyak konteks tentang apa yang Anda coba sarankan
MM
2

Jawaban umum cepat:

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

atau pertanyaan khusus:

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

berdasarkan di atas : Jangan lupa bahwa Anda dapat memperlakukan pointer sebagai iterator

eusoubrasileiro
sumber
0

std::vector<double>::assignadalah cara untuk pergi, karena itu kode kecil . Tapi bagaimana cara kerjanya, sebenarnya? Bukankah itu mengubah ukuran dan kemudian menyalin? Dalam implementasi MS STL saya menggunakannya tidak persis seperti itu.

Saya khawatir tidak ada cara yang lebih cepat untuk menerapkan (re) menginisialisasi Anda std::vector.

Janusz Lenar
sumber
bagaimana jika data dibagikan antara vektor dan array? Apakah kita perlu menyalin sesuatu dalam hal ini?
Vlad
apakah itu jawaban atau pertanyaan? apa yang ia bawa ke jawaban yang sudah ada?
Jean-François Fabre
@ Jean-FrançoisFabre dan apa komentar Anda? ;) benar, itu adalah jawaban buruk yang diberikan berabad-abad lalu.
Janusz Lenar