Saya menggunakan perpustakaan eksternal yang pada beberapa titik memberi saya pointer mentah ke array bilangan bulat dan ukuran.
Sekarang saya ingin menggunakan std::vector
untuk mengakses dan memodifikasi nilai-nilai ini di tempat, daripada mengaksesnya dengan pointer mentah.
Berikut adalah contoh artikifial yang menjelaskan intinya:
size_t size = 0;
int * data = get_data_from_library(size); // raw data from library {5,3,2,1,4}, size gets filled in
std::vector<int> v = ????; // pseudo vector to be used to access the raw data
std::sort(v.begin(), v.end()); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
Output yang diharapkan:
1
2
3
4
5
Alasannya adalah bahwa saya perlu menerapkan algoritma dari <algorithm>
(menyortir, menukar elemen dll) pada data itu.
Di sisi lain mengubah ukuran vektor yang tidak akan pernah berubah, jadi push_back
, erase
, insert
tidak diharuskan untuk bekerja pada vektor itu.
Saya bisa membuat vektor berdasarkan data dari perpustakaan, gunakan memodifikasi vektor itu dan menyalin data kembali ke perpustakaan, tapi itu akan menjadi dua salinan lengkap yang ingin saya hindari karena kumpulan data bisa sangat besar.
std::vector_view
, bukan?std::vector
kerjanya.sort(arrayPointer, arrayPointer + elementCount);
.Jawaban:
Masalahnya adalah bahwa
std::vector
harus membuat salinan elemen dari array yang Anda inisialisasi karena memiliki kepemilikan objek yang dikandungnya.Untuk menghindari ini, Anda bisa menggunakan objek slice untuk array (yaitu, mirip dengan apa
std::string_view
yang harusstd::string
). Anda bisa menulisarray_view
implementasi templat kelas Anda sendiri yang instansinya dibangun dengan mengambil pointer mentah ke elemen pertama array dan panjang array:array_view
tidak menyimpan array; itu hanya memegang pointer ke awal array dan panjang array itu. Oleh karena itu,array_view
objek murah untuk dikonstruksi dan disalin.Karena
array_view
menyediakanbegin()
danend()
anggota fungsi, Anda dapat menggunakan algoritma standar perpustakaan (misalnya,std::sort
,std::find
,std::lower_bound
, dll) di atasnya:Keluaran:
Gunakan
std::span
(ataugsl::span
) sebagai gantinyaImplementasi di atas memperlihatkan konsep di balik objek slice . Namun, karena C ++ 20 Anda dapat langsung menggunakannya
std::span
. Bagaimanapun, Anda dapat menggunakangsl::span
sejak C ++ 14.sumber
C ++ 20-an
std::span
Jika Anda bisa menggunakan C ++ 20, Anda bisa menggunakan
std::span
pasangan panjang-penunjuk yang memberi pengguna tampilan ke urutan elemen yang berdekatan. Ini adalah semacamstd::string_view
, dan meskipun keduanyastd::span
danstd::string_view
tidak memiliki tampilan,std::string_view
adalah tampilan hanya baca.Dari dokumen:
Jadi yang berikut ini akan berhasil:
Lihat langsung
Karena
std::span
pada dasarnya adalah pasangan pointer-panjang, Anda dapat menggunakan dengan cara berikut juga:catatan: Tidak semua dukungan kompiler
std::span
. Periksa dukungan kompiler di sini .MEMPERBARUI
Jika Anda tidak dapat menggunakan C ++ 20, Anda bisa menggunakan
gsl::span
yang pada dasarnya adalah versi dasar dari standar C ++std::span
.Solusi C ++ 11
Jika Anda terbatas pada standar C ++ 11, Anda dapat mencoba menerapkan
span
kelas sederhana Anda sendiri :Lihat versi C ++ 11 langsung
sumber
gsl::span
untuk C ++ 14 dan di atasnya jika kompiler Anda tidak mengimplementasikanstd::span
Karena pustaka algoritma bekerja dengan iterator, Anda dapat menyimpan array.
Untuk pointer dan panjang array yang diketahui
Di sini Anda dapat menggunakan pointer mentah sebagai iterator. Mereka mendukung semua operasi yang didukung oleh iterator (kenaikan, perbandingan untuk kesetaraan, nilai, dll ...):
data
menunjuk ke anggota array dirst seperti iterator yang dikembalikan olehbegin()
dandata + size
menunjuk ke elemen setelah elemen terakhir array seperti iterator dikembalikan olehend()
.Untuk array
Di sini Anda dapat menggunakan
std::begin()
danstd::end()
Tetapi perlu diingat bahwa ini hanya berfungsi, jika
data
tidak membusuk ke sebuah pointer, karena informasi panjang itu hilang.sumber
Anda bisa mendapatkan iterator pada array mentah dan menggunakannya dalam algoritma:
Jika Anda bekerja dengan pointer mentah (ptr + size), maka Anda dapat menggunakan teknik berikut:
UPD: Namun, contoh di atas adalah desain yang buruk. Perpustakaan mengembalikan kita pointer mentah dan kita tidak tahu di mana buffer yang mendasarinya dialokasikan dan siapa yang seharusnya membebaskannya.
Biasanya, penelepon menyediakan buffered untuk fungsi untuk mengisi data. Dalam hal ini, kita dapat melakukan prealokasi vektor dan menggunakan buffer yang mendasarinya:
Saat menggunakan C ++ 11 atau lebih tinggi kita bahkan dapat membuat get_data_from_library () untuk mengembalikan vektor. Berkat memindahkan operasi, tidak akan ada salinan memori.
sumber
auto begin = data;
auto end = data + size;
get_data_from_library()
dialokasikan? Mungkin kita tidak seharusnya mengubahnya sama sekali. Jika kita perlu meneruskan buffer ke perpustakaan, maka kita dapat mengalokasikan vektor dan lulusv.data()
Anda tidak dapat melakukan ini dengan
std::vector
tanpa membuat salinan.std::vector
memiliki pointer yang dimilikinya di bawah kap dan mengalokasikan ruang melalui pengalokasi yang disediakan.Jika Anda memiliki akses ke kompiler yang memiliki dukungan untuk C ++ 20 yang dapat Anda gunakan std :: span yang dibangun untuk tujuan ini. Ini membungkus pointer dan ukuran menjadi "wadah" yang memiliki antarmuka wadah C ++.
Jika tidak, Anda bisa menggunakannya gsl :: span yang merupakan dasar dari versi standar.
Jika Anda tidak ingin mengimpor perpustakaan lain, Anda sendiri dapat menerapkannya sendiri tergantung pada semua fungsi yang ingin Anda miliki.
sumber
Kamu tidak bisa. Bukan itu untuk apa
std::vector
.std::vector
mengelola buffer sendiri, yang selalu diperoleh dari pengalokasi. Tidak pernah mengambil kepemilikan buffer lain (kecuali dari vektor lain dengan jenis yang sama).Di sisi lain, Anda juga tidak perlu karena ...
Algoritma tersebut bekerja pada iterator. Pointer adalah iterator ke array. Anda tidak perlu vektor:
Tidak seperti templat fungsi
<algorithm>
, beberapa alat seperti rentang-untuk,std::begin
/std::end
dan rentang C ++ 20 tidak berfungsi hanya dengan sepasang iterator, sementara mereka bekerja dengan wadah seperti vektor. Dimungkinkan untuk membuat kelas pembungkus untuk ukuran iterator + yang berperilaku sebagai rentang, dan bekerja dengan alat ini. C ++ 20 akan memperkenalkan wrapper tersebut ke perpustakaan standar:std::span
.sumber
Selain saran bagus lainnya tentang
std::span
datang di c ++ 20 dangsl:span
, termasuk kelas Anda sendiri (ringan)span
sampai saat itu sudah cukup mudah (jangan ragu untuk menyalin):Catatan khusus juga merupakan peningkatan jangkauan perpustakaan rentang jika Anda tertarik pada konsep rentang yang lebih umum: https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference /utilities/iterator_range.html .
Konsep rentang juga akan tiba di c ++ 20
sumber
using value_type = std::remove_cv_t<T>;
?span(T* first_, size_t length) : first(first), length(length) {};
. Saya mengedit jawaban Anda.using value_type = std::remove_cv_t<T>;
terutama diperlukan jika digunakan dengan pemrograman template (untuk mendapatkan value_type dari 'range'). Jika Anda hanya ingin menggunakan iterator, Anda dapat melewati / menghapusnya.Anda sebenarnya hampir dapat menggunakannya
std::vector
untuk ini, dengan menyalahgunakan fungsi pengalokasi khusus untuk mengembalikan pointer ke memori yang ingin Anda lihat. Itu tidak akan dijamin oleh standar untuk bekerja (padding, alignment, inisialisasi nilai yang dikembalikan; Anda harus bersusah payah ketika menetapkan ukuran awal, dan untuk non-primitif Anda juga perlu meretas konstruktor Anda ), tetapi dalam prakteknya saya berharap untuk memberikan tweak yang cukup.Tidak pernah melakukannya. Itu jelek, mengejutkan, peretasan, dan tidak perlu. Algoritme pustaka standar sudah dirancang untuk bekerja juga dengan array mentah seperti halnya dengan vektor. Lihat jawaban lain untuk detailnya.
sumber
vector
konstruktor yang mengambil referensi Allocator kustom sebagai konstruktor arg (bukan hanya param template). Saya kira Anda akan memerlukan objek pengalokasi yang memiliki nilai pointer runtime di dalamnya, bukan sebagai parameter template kalau tidak itu hanya bisa bekerja untuk alamat constexpr. Anda harus berhati-hati untuk tidak membiarkan objekvector
-objek bawaan dibangun.resize()
dan menimpa data yang ada; ketidakcocokan antara wadah yang memiliki seperti vektor vs rentang yang tidak memiliki sangat besar jika Anda mulai menggunakan .push_back dllconstruct
metode yang akan diperlukan ... Saya tidak dapat berpikir kasus penggunaan apa yang memerlukan non-hacky yang memerlukan penempatan lebih baru.resize()
sebelum Anda meneruskan referensi ke sesuatu yang ingin menggunakannya sebagai output murni (misalnya panggilan sistem baca). Dalam praktiknya kompiler sering tidak mengoptimalkan memset itu atau apa pun. Atau jika Anda memiliki pengalokasi yang menggunakan calloc untuk mendapatkan memori pra-zeroed, Anda juga bisa menghindari mengotori seperti yang dilakukan secara bodohstd::vector<int>
ketika default-membangun objek yang memiliki pola bit semua-nol. Lihat Catatan di en.cppreference.com/w/cpp/container/vector/vectorSeperti yang telah ditunjukkan orang lain,
std::vector
harus memiliki memori yang mendasarinya (tidak dapat digunakan untuk pengalokasi khusus) sehingga tidak dapat digunakan.Yang lain juga merekomendasikan rentang c ++ 20, namun jelas yang membutuhkan c ++ 20.
Saya akan merekomendasikan rentang span-lite . Mengutip subtitle:
Ini memberikan tampilan yang tidak memiliki dan dapat diubah (seperti pada Anda dapat mengubah elemen dan urutannya tetapi tidak memasukkannya) dan seperti kata kutipan tidak memiliki dependensi dan berfungsi pada sebagian besar kompiler.
Contoh Anda:
Cetakan
Ini juga memiliki sisi positif yang ditambahkan jika suatu hari Anda beralih ke c ++ 20, Anda seharusnya hanya dapat menggantinya
nonstd::span
denganstd::span
.sumber
Anda dapat menggunakan yang
std::reference_wrapper
tersedia sejak C ++ 11:sumber
std::copy(std::begin(src_table), std::end(src_table), std::back_inserter(dest_vector));
jelas mengisidest_vector
dengan nilai yang diambil darisrc_table
(TKI datanya disalindest_vector
), jadi saya tidak mendapatkan komentar Anda. Bisakah Anda jelaskan?