Setiap kontainer standar memiliki begin
dan end
metode untuk mengembalikan iterator untuk wadah itu. Namun, C ++ 11 tampaknya telah memperkenalkan fungsi bebas yang dipanggil std::begin
dan std::end
yang memanggil fungsi begin
dan end
anggota. Jadi, alih-alih menulis
auto i = v.begin();
auto e = v.end();
kamu akan menulis
auto i = std::begin(v);
auto e = std::end(v);
Dalam ceramahnya, Writing Modern C ++ , Herb Sutter mengatakan bahwa Anda harus selalu menggunakan fungsi gratis sekarang ketika Anda ingin memulai atau mengakhiri iterator untuk sebuah wadah. Namun, ia tidak menjelaskan secara terperinci mengapa Anda menginginkannya. Melihat kode itu, Anda menghemat satu karakter. Jadi, sejauh kontainer standar berjalan, fungsi bebas tampaknya sama sekali tidak berguna. Herb Sutter menunjukkan bahwa ada manfaat untuk wadah non-standar, tetapi sekali lagi, dia tidak menjelaskan secara rinci.
Jadi, pertanyaannya adalah apa tepatnya versi fungsi bebas std::begin
dan std::end
lakukan di luar memanggil versi fungsi anggota yang sesuai, dan mengapa Anda ingin menggunakannya?
std::
sepanjang waktu.Jawaban:
Bagaimana Anda memanggil
.begin()
dan.end()
menggunakan C-array?Fungsi bebas memungkinkan pemrograman yang lebih umum karena mereka dapat ditambahkan sesudahnya, pada struktur data yang tidak dapat Anda ubah.
sumber
end
array yang dideklarasikan secara statis (int foo[5]
) menggunakan trik pemrograman template. Setelah membusuk menjadi pointer, Anda tentu saja kurang beruntung.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
danend
menggunakan array C selama Anda belum membukanya sendiri ke sebuah pointer - @Huw menguraikannya. Adapun mengapa Anda ingin: bayangkan bahwa Anda refactored kode yang menggunakan array untuk menggunakan vektor (atau sebaliknya, untuk alasan apa pun). Jika Anda telah menggunakanbegin
danend
, dan mungkin beberapa kesalahan ketik yang pintar, kode implementasi tidak harus berubah sama sekali (kecuali mungkin beberapa dari typedefs).Pertimbangkan kasus ketika Anda memiliki perpustakaan yang berisi kelas:
ini memiliki 2 metode:
untuk mengulangi nilai-nilai itu Anda perlu mewarisi dari kelas ini dan menentukan
begin()
danend()
metode untuk kasus saatTetapi jika Anda selalu menggunakan
kamu bisa melakukan ini:
di mana
SpecialArrayIterator
sesuatu seperti:sekarang
i
dane
dapat secara hukum digunakan untuk iterasi dan mengakses nilai-nilai SpecialArraysumber
template<>
garis. Anda mendeklarasikan kelebihan fungsi baru, bukan spesialisasi template.Menggunakan
begin
danend
fungsi bebas menambahkan satu lapisan tipuan. Biasanya itu dilakukan untuk memungkinkan lebih banyak fleksibilitas.Dalam hal ini saya bisa memikirkan beberapa kegunaan.
Penggunaan yang paling jelas adalah untuk C-array (bukan c pointer).
Lain adalah ketika mencoba menggunakan algoritma standar pada wadah yang tidak sesuai (yaitu wadah tersebut tidak memiliki
.begin()
metode). Dengan asumsi Anda tidak bisa hanya memperbaiki wadah, opsi terbaik berikutnya adalah membebanibegin
fungsinya. Ramuan menyarankan Anda selalu menggunakanbegin
fungsi untuk mempromosikan keseragaman dan konsistensi dalam kode Anda. Daripada harus mengingat wadah mana yang mendukung metodebegin
dan mana yang perlu berfungsibegin
.Sebagai samping, C ++ berikutnya rev harus menyalin D's notasi pseudo-anggota . Jika
a.foo(b,c,d)
tidak didefinisikan sebagai gantinya mencobafoo(a,b,c,d)
. Hanya sedikit sintaksis gula untuk membantu kita manusia miskin yang lebih suka subjek daripada memesan kata kerja.sumber
Untuk menjawab pertanyaan Anda, fungsi bebas memulai () dan mengakhiri () secara default tidak melakukan apa-apa selain memanggil fungsi anggota .begin () dan .end (). Dari
<iterator>
, termasuk secara otomatis ketika Anda menggunakan salah satu wadah standar seperti<vector>
,<list>
, dll, Anda mendapatkan:Bagian kedua dari pertanyaan Anda adalah mengapa memilih fungsi gratis jika yang mereka lakukan hanyalah memanggil fungsi anggota. Itu benar-benar tergantung pada objek apa yang
v
ada dalam kode contoh Anda. Jika tipe v adalah tipe kontainer standar, sepertivector<T> v;
maka tidak masalah jika Anda menggunakan fungsi bebas atau anggota, mereka melakukan hal yang sama. Jika objek Andav
lebih umum, seperti dalam kode berikut:Kemudian menggunakan fungsi anggota memecah kode Anda untuk T = C array, string C, enum, dll. Dengan menggunakan fungsi non-anggota, Anda mengiklankan antarmuka yang lebih umum yang orang dapat dengan mudah memperluas. Dengan menggunakan antarmuka fungsi gratis:
Kode sekarang bekerja dengan T = C array dan string C. Sekarang menulis sejumlah kecil kode adaptor:
Kami bisa membuat kode Anda agar kompatibel dengan enum yang dapat diubah juga. Saya pikir poin utama Herb adalah bahwa menggunakan fungsi bebas sama mudahnya dengan menggunakan fungsi anggota, dan itu memberikan kode Anda kompatibilitas mundur dengan jenis urutan C dan kompatibilitas maju dengan jenis urutan non-stl (dan jenis-stl masa depan!), dengan biaya rendah untuk pengembang lain.
sumber
enum
atau jenis fundamental lainnya dengan referensi, meskipun; mereka akan lebih murah untuk menyalin daripada tidak langsung.Salah satu manfaat
std::begin
danstd::end
adalah mereka berfungsi sebagai titik ekstensi untuk mengimplementasikan antarmuka standar untuk kelas eksternal.Jika Anda ingin menggunakan
CustomContainer
kelas dengan rentang berbasis untuk fungsi loop atau templat yang diharapkan.begin()
dan.end()
metode, Anda jelas harus menerapkan metode tersebut.Jika kelas memang menyediakan metode tersebut, itu tidak masalah. Jika tidak, Anda harus memodifikasinya *.
Ini tidak selalu layak, misalnya ketika menggunakan perpustakaan eksternal, terutama yang komersial dan sumber tertutup.
Dalam situasi seperti itu,
std::begin
danstd::end
sangat berguna, karena seseorang dapat menyediakan API iterator tanpa memodifikasi kelas itu sendiri, tetapi kelebihan fungsi bebas.Contoh: misalkan Anda ingin menerapkan
count_if
fungsi yang mengambil wadah alih-alih sepasang iterator. Kode tersebut mungkin terlihat seperti ini:Sekarang, untuk setiap kelas yang ingin Anda gunakan dengan kebiasaan ini
count_if
, Anda hanya perlu menambahkan dua fungsi gratis, daripada memodifikasi kelas-kelas itu.Sekarang, C ++ memiliki mechanisim yang disebut Argument Dependent Lookup (ADL), yang membuat pendekatan seperti itu bahkan lebih fleksibel.
Singkatnya, ADL berarti, bahwa ketika kompiler menyelesaikan fungsi yang tidak memenuhi syarat (yaitu fungsi tanpa namespace, seperti
begin
bukanstd::begin
), ia juga akan mempertimbangkan fungsi yang dideklarasikan dalam ruang nama argumen. Sebagai contoh:Dalam hal ini, tidak masalah bahwa nama yang memenuhi syarat adalah
some_lib::begin
dansome_lib::end
- karenaCustomContainer
adasome_lib::
juga, kompiler akan menggunakan kelebihan itu dicount_if
.Itu juga alasan untuk memiliki
using std::begin;
danusing std::end;
masukcount_if
. Ini memungkinkan kita untuk menggunakan tanpa pengecualianbegin
danend
, karenanya memungkinkan ADL dan memungkinkan kompiler untuk memilihstd::begin
danstd::end
ketika tidak ada alternatif lain ditemukan.Kita bisa makan cookie dan memiliki cookie - yaitu memiliki cara untuk menyediakan implementasi kustom
begin
/end
sementara kompiler dapat kembali ke yang standar.Beberapa catatan:
Untuk alasan yang sama, ada fungsi serupa lainnya:
std::rbegin
/rend
,std::size
danstd::data
.Seperti jawaban lain menyebutkan,
std::
versi memiliki kelebihan untuk array kosong. Itu berguna, tetapi hanya kasus khusus dari apa yang saya jelaskan di atas.Menggunakan
std::begin
dan berteman adalah ide yang bagus ketika menulis kode templat, karena ini membuat templat-templat tersebut lebih umum. Untuk non-templat Anda mungkin juga menggunakan metode, jika berlaku.PS Saya tahu bahwa postingan ini sudah hampir 7 tahun. Saya menemukan itu karena saya ingin menjawab pertanyaan yang ditandai sebagai duplikat dan menemukan bahwa tidak ada jawaban di sini yang menyebutkan ADL.
sumber
Sedangkan fungsi non-anggota tidak memberikan manfaat apa pun untuk wadah standar, menggunakannya tidak memberikan gaya yang lebih konsisten dan fleksibel. Jika suatu saat Anda ingin memperluas kelas penampung non-std yang ada, Anda lebih suka mendefinisikan kelebihan fungsi-fungsi gratis, daripada mengubah definisi kelas yang ada. Jadi untuk wadah non-std mereka sangat berguna dan selalu menggunakan fungsi bebas membuat kode Anda lebih fleksibel karena Anda dapat mengganti wadah std dengan wadah non-std lebih mudah dan jenis wadah yang mendasarinya lebih transparan untuk kode Anda karena mendukung beragam implementasi kontainer yang jauh lebih luas.
Tapi tentu saja ini harus selalu ditimbang dengan benar dan abstraksi yang berlebihan juga tidak baik. Meskipun menggunakan fungsi-fungsi gratis tidak terlalu banyak abstraksi, itu tetap merusak kompatibilitas dengan kode C ++ 03, yang pada usia muda ini C ++ 11 mungkin masih menjadi masalah bagi Anda.
sumber
boost::begin()
/end()
, jadi tidak ada ketidakcocokan nyata :)begin/end
). Jadi saya akan menganggap bahwa ketidakcocokan dengan C + + 03 murni juga. Tapi seperti dikatakan, itu adalah ketidakcocokan yang agak kecil (dan semakin kecil), karena C ++ 11 (setidaknyabegin/end
khususnya) semakin banyak diadopsi.Pada akhirnya manfaatnya adalah dalam kode yang digeneralisasi sehingga menjadi wadah agnostik. Itu dapat beroperasi pada
std::vector
, array, atau rentang tanpa perubahan pada kode itu sendiri.Selain itu, kontainer, bahkan kontainer yang tidak dimiliki dapat dipasang sedemikian rupa sehingga mereka juga dapat digunakan secara agnostik dengan kode menggunakan pengakses berbasis rentang non-anggota.
Lihat di sini untuk detail lebih lanjut.
sumber