Sekarang kita memiliki std :: array, apa kegunaan yang tersisa untuk array C-style?

89

std::arrayjauh lebih unggul dari array C. Dan bahkan jika saya ingin bekerja sama dengan kode lama, saya cukup menggunakan std::array::data(). Apakah ada alasan mengapa saya menginginkan array jadul?

R. Martinho Fernandes
sumber

Jawaban:

60

Kecuali saya telah melewatkan sesuatu (saya tidak mengikuti perubahan terbaru dalam standar terlalu dekat), sebagian besar penggunaan array gaya C masih tetap ada. std::arraymemungkinkan inisialisasi statis, tetapi masih tidak akan menghitung penginisialisasi untuk Anda. Dan karena satu-satunya penggunaan sebenarnya dari array gaya C sebelumnya std::arrayadalah untuk tabel yang diinisialisasi secara statis di sepanjang baris:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

menggunakan fungsi biasa begindan endtemplate (diadopsi dalam C ++ 11) untuk mengulanginya. Tanpa pernah menyebutkan ukuran, yang ditentukan oleh compiler dari jumlah penginisialisasi.

EDIT: Hal lain yang saya lupa: string literal masih array gaya C; yaitu dengan tipe char[]. Saya tidak berpikir ada orang yang akan mengecualikan penggunaan string literal hanya karena kita punya std::array.

James Kanze
sumber
7
Anda dapat menulis template fungsi variadic yang membangun array tanpa Anda harus menentukan panjangnya.
kanan
2
Dengan C ++ 17 Pengurangan Template Kelas, pengurangan otomatis dari jumlah penginisialisasi didukung. Misalnya, "auto a = std :: array {1, 2, 3};"
Ricky65
Nitpick: jenis literal string adalahconst char[]
Bulletmagnet
31

Tidak Untuk, uh, terus terang. Dan dalam 30 karakter.

Tentu saja, Anda memerlukan array C untuk diimplementasikan std::array, tetapi itu sebenarnya bukan alasan bahwa pengguna menginginkan array C. Selain itu, tidak, std::arrayperformanya tidak kalah dengan array C, dan memiliki opsi untuk akses yang diperiksa batasnya. Dan akhirnya, sangat masuk akal untuk program C ++ apa pun untuk bergantung pada pustaka Standar- itulah intinya adalah Standar- dan jika Anda tidak memiliki akses ke pustaka Standar, maka kompiler Anda tidak sesuai dan pertanyaan diberi tag "C ++", bukan "C ++ dan hal-hal bukan-C ++ yang kehilangan separuh spesifikasi karena merasa tidak sesuai.".

Anak anjing
sumber
1
Hm. Bagaimana jika Anda menulis kode C ++ yang dipanggil dari bahasa lain dan membutuhkan sesuatu untuk diteruskan sebagai parameter?
asveikau
3
Implementasi berdiri bebas diizinkan untuk meninggalkan hampir semua pustaka standar dan masih sesuai. Saya akan memiliki keraguan serius tentang kompiler yang tidak dapat diimplementasikan std::arraydalam implementasi C ++ 11 yang berdiri sendiri.
Dennis Zickefoose
11
C ++ 0x Final Draft (Document N3092) § 1.4.7 "Ada dua jenis implementasi yang didefinisikan: di-host dan berdiri bebas. Untuk implementasi yang di-host, Standar Internasional ini mendefinisikan kumpulan pustaka yang tersedia. Implementasi berdiri bebas adalah implementasi di mana eksekusi dapat berlangsung tanpa manfaat dari sistem operasi, dan memiliki seperangkat pustaka yang ditetapkan implementasi yang mencakup pustaka dukungan bahasa tertentu ".. STL tidak disertakan sebagai pustaka" dukungan bahasa "dalam kompiler berdiri bebas
Earlz
24

Sepertinya menggunakan multi-dimensi array lebih mudah dengan C array dari std::array. Contohnya,

char c_arr[5][6][7];

sebagai lawan

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Juga karena properti peluruhan otomatis dari array C, c_arr[i]dalam contoh di atas akan meluruh menjadi penunjuk dan Anda hanya perlu meneruskan dimensi yang tersisa sebagai dua parameter lagi. Maksud saya c_arradalah tidak mahal untuk menyalin. Namun, cpp_arr[i]akan sangat mahal untuk menyalinnya.

Sumant
sumber
1
Namun, Anda bisa meneruskan multidimensi arrayke fungsi tanpa kehilangan dimensi. Dan jika Anda meneruskannya ke template fungsi, maka fungsi tersebut dapat menyimpulkan dimensi dan ukuran setiap dimensi, atau hanya salah satu dari keduanya. Ini mungkin menarik untuk pustaka template ilmiah yang pada prinsipnya bekerja pada dimensi arbitrer.
Sebastian Mach
30
sederhana template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;harus menyelesaikan salah satu dari masalah tersebut.
Miles Rout
6
Misalnya Anda c_arradalah sangat mahal untuk menyalin! Anda harus memberikan kode untuk melakukannya sendiri. Penunjuk yang akan meluruhnya lebih mirip dengan referensi daripada salinan dan Anda dapat menggunakan std::arrayuntuk meneruskan referensi jika itu yang Anda inginkan.
ChetS
1
@MilesRout teknis , tidak harus yang menjadi std::size_tbukan int? maaf untuk nitpicking, tapi ini akan membuatnya universal.
robbie
1
@ robbie0630 Ya, Anda dapat membuatnya size_tjika Anda mau, walaupun saya tidak dapat membayangkan ada banyak skenario di mana diperlukan array dengan lebih dari 4 miliar baris atau kolom.
Rute Miles
14

Seperti yang dikatakan Sumant, array multi-dimensi jauh lebih mudah digunakan dengan array-C bawaan daripada dengan std::array.

Saat bersarang, std::arraybisa menjadi sangat sulit untuk dibaca dan bertele-tele yang tidak perlu.

Sebagai contoh:

std::array<std::array<int, 3>, 3> arr1; 

dibandingkan dengan

char c_arr[3][3]; 

Juga, perhatikan itu begin(), end()dan size()semua mengembalikan nilai yang tidak berarti saat Anda menumpuk std::array.

Untuk alasan ini saya telah membuat wadah array multidimensi ukuran tetap saya sendiri, array_2ddan array_3d. Mereka analog std::arraytetapi untuk array multidimensi 2 dan 3 dimensi. Mereka lebih aman dan tidak memiliki kinerja yang lebih buruk daripada array multidimensi bawaan. Saya tidak menyertakan wadah untuk array multidimensi dengan dimensi lebih besar dari 3 karena tidak umum. Dalam C ++ 0x, versi template variadic dapat dibuat yang mendukung jumlah dimensi yang berubah-ubah.

Contoh varian dua dimensi:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

Dokumentasi lengkap tersedia di sini:

http://fsma.googlecode.com/files/fsma.html

Anda dapat mengunduh perpustakaan di sini:

http://fsma.googlecode.com/files/fsma.zip

Ricky65
sumber
4
Larik C-style ukuran tetap itu mudah, tetapi jika Anda ingin memvariasikan dimensi, semuanya menjadi rumit. Sebagai contoh, jika diberikan arr[x][y], Anda tidak dapat membedakan apakah arrarray array, array pointer, pointer ke array, atau pointer ke pointer; semua untuk implementasi sah, tergantung pada kebutuhan Anda. Dan mungkin sebagian besar kasus penggunaan dunia nyata untuk array multidimensi memerlukan ukuran yang ditentukan pada waktu proses.
Keith Thompson
Saya ingin sekali melihat implementasi variadic-template dari array n-dimensional! Meta-pemrograman yang terbaik!
steffen
3
@steffen Saya melakukan percobaan beberapa tahun yang lalu. Anda dapat melihatnya di sini: code.google.com/p/fsma/source/browse/trunk/… . Uji kasus di sini: code.google.com/p/fsma/source/browse/trunk/… . Saya yakin itu bisa dilakukan jauh lebih baik.
Ricky65
5

Array C-style yang tersedia di C ++ sebenarnya jauh lebih tidak fleksibel daripada C-array yang sebenarnya. Perbedaannya adalah, di C, tipe array bisa memiliki ukuran runtime . Berikut ini adalah kode C yang valid, tetapi tidak dapat diekspresikan dengan larik gaya C ++ C maupun dengan jenis C ++ array<>:

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

Di C ++, Anda harus mengalokasikan array sementara di heap:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Ini tidak dapat dicapai dengan std::array<>, karena bartidak diketahui pada waktu kompilasi, ini memerlukan penggunaan array gaya-C di C ++ atau dari std::vector<>.


Sementara contoh pertama relatif mudah diekspresikan dalam C ++ (meskipun membutuhkan new[]dan delete[]), berikut ini tidak dapat dicapai dalam C ++ tanpa std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Intinya adalah, pointer ke line array int (*)[width]tidak dapat menggunakan lebar runtime di C ++, yang membuat kode manipulasi gambar menjadi jauh lebih rumit di C ++ daripada di C. Implementasi C ++ khas dari contoh manipulasi gambar akan terlihat seperti ini:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Kode ini melakukan penghitungan yang persis sama dengan kode C di atas, tetapi kode ini perlu melakukan penghitungan indeks secara manual di mana pun indeks digunakan . Untuk kasus 2D, hal ini masih dapat dilakukan (meskipun memiliki banyak peluang untuk salah menghitung indeks). Ini menjadi sangat buruk dalam kasus 3D.

Saya suka menulis kode dalam C ++. Tetapi setiap kali saya perlu memanipulasi data multidimensi, saya benar-benar bertanya pada diri sendiri apakah saya harus memindahkan bagian kode itu ke C.

cmaster - kembalikan monica
sumber
7
Perlu diperhatikan bahwa setidaknya Clang dan GCC mendukung VLA di C ++.
Janus Troelsen
@JanusTroelsen dan juga bahwa mereka sangat terbatas dalam jenis elemen yang mereka dukung.
kanan
Bukankah C11 menjadikan VLA opsional? Jika demikian maka saya pikir jawaban Anda menyesatkan. Akan benar jika C99 adalah standar tetapi bukan C11.
Z boson
1
@Zboson C99 adalah standar C, dan ada kompiler yang mengimplementasikan fitur VLA-nya ( gccmisalnya). C11 telah membuat beberapa hal menarik menjadi opsional, dan saya rasa itu bukan karena mereka ingin melarang fitur tersebut. Saya cenderung melihatnya sebagai tanda bahwa mereka ingin menurunkan level untuk menulis kompiler yang memenuhi standar sepenuhnya: VLA adalah binatang yang cukup sulit untuk diterapkan, dan banyak kode dapat melakukannya tanpa, jadi masuk akal untuk kompiler baru pada beberapa yang baru platform untuk tidak harus segera mengimplementasikan VLA.
cmaster
-1

Mungkin std::arraytidak lambat. Tapi saya melakukan beberapa benchmarking menggunakan simple store dan membaca dari std :: array; Lihat hasil benchmark di bawah ini (pada W8.1, VS2013 Update 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Menurut tanda negatifnya, kode yang saya gunakan ada di pastebin ( tautan )

Kode kelas patokan ada di sini ;

Saya tidak tahu banyak tentang pembandingan ... Kode saya mungkin cacat

K'Prime
sumber
6
Hasil benchmark tanpa kode benchmark, atau flag kompilasi? Ayo, kamu bisa lebih baik.
R. Martinho Fernandes
FWIW, hanya sedikit kode yang sudah menunjukkan benchmark sangat cacat. Kompiler yang cukup cerdas hanya akan mengubah semuanya menjadilong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes
Itu hanya sebuah contoh. Saya pikir itu bukan masalah besar. Saya mengubah kode untuk mengembalikan void, menggunakan rilis build di VS 2013, dengan / O2 / Ot / Gl.
K'Prime
Menghapus nilai kembali berarti kompilator dapat mengubah semuanya menjadi void test_arr_without_init() {}sekarang. Anda benar-benar perlu melewati rintangan untuk memastikan kode yang Anda ukur adalah kode yang ingin Anda ukur.
R. Martinho Fernandes
-5
  1. untuk menerapkan sesuatu seperti std::array
  2. jika Anda tidak ingin menggunakan STL atau tidak bisa
  3. Untuk performa
Lou Franco
sumber
27
Katakan padaku bagaimana std::arrayakan menjadi kurang berkinerja dari array C.
Xeo
2
Dari wikipedia : "Implementasi array tidak diperlukan untuk melakukan pemeriksaan terikat. Namun implementasi dalam boost melakukannya untuk operator [], tetapi tidak untuk iterator." - jadi operator [] lebih lambat. Saya belum melihat implementasi, tetapi kode apa pun dalam implementasi dapat menghalangi pengoptimal.
Lou Franco
19
@ Aaron McDaid: Itu hanya masuk at(), tidak masuk operator[], sama seperti std::vector. Tidak ada penurunan kinerja atau kode yang membengkak std::array, kompilator dirancang untuk mengoptimalkan hal semacam ini. Dan, tentu saja, penambahan fungsi yang dicentang merupakan alat debug yang sangat baik dan keuntungan besar. @ Lou Franco: Semua kode C ++ mungkin bergantung pada pustaka Standar- untuk itulah. @Earlz: Jika Anda tidak memiliki STL yang tersedia, maka itu bukan C ++, dan itulah akhir dari itu.
Anak anjing
6
@Earlz: C ++ Standard berisi pustaka Standar. Jika Anda tidak dapat menggunakan perpustakaan, itu tidak sesuai. Dan kedua, Anda harus memiliki kompiler yang sangat buruk untuk digunakan std::arrayagar lebih besar dari penggunaan array C yang setara.
Anak anjing
5
@Earlz: Ada perbedaan besar antara "tidak cukup sesuai" dan "fitur yang hilang dengan spesifikasi ratusan halaman".
Anak anjing