Apakah ada kelebihan std::for_each
over for
loop? Bagi saya, std::for_each
sepertinya hanya menghambat pembacaan kode. Mengapa beberapa standar pengkodean merekomendasikan penggunaannya?
c++
stl
foreach
coding-style
missingfaktor
sumber
sumber
std::for_each
ketika digunakan denganboost.lambda
atauboost.bind
sering dapat meningkatkan keterbacaanJawaban:
Yang menyenangkan dengan C ++ 11 (sebelumnya disebut C ++ 0x), adalah bahwa perdebatan yang melelahkan ini akan diselesaikan.
Maksudku, tidak ada yang waras, yang ingin mengulang seluruh koleksi, masih akan menggunakan ini
Atau ini
ketika sintaks loop berbasis rentang
for
tersedia:Sintaks semacam ini telah tersedia di Java dan C # untuk beberapa waktu sekarang, dan sebenarnya ada lebih banyak
foreach
loop daripadafor
loop klasik di setiap kode Java atau C # yang saya lihat baru-baru ini.sumber
Element & e
sebagaiauto & e
(atauauto const &e
) terlihat lebih baik. Saya akan menggunakanElement const e
(tanpa referensi) ketika saya ingin konversi implisit, katakan ketika sumbernya adalah kumpulan dari berbagai jenis, dan saya ingin mereka mengubahnya menjadiElement
.Berikut beberapa alasannya:
Tampaknya menghambat keterbacaan hanya karena Anda tidak terbiasa dan / atau tidak menggunakan alat yang tepat di sekitarnya untuk membuatnya sangat mudah. (lihat boost :: range dan boost :: bind / boost :: lambda untuk helpers. Banyak di antaranya akan masuk ke C ++ 0x dan membuat for_each dan fungsi terkait lebih bermanfaat.)
Hal ini memungkinkan Anda untuk menulis algoritma di atas for_each yang bekerja dengan iterator apa pun.
Ini mengurangi kemungkinan bug pengetikan yang bodoh.
Hal ini juga membuka pikiran Anda untuk sisa STL-algoritma, seperti
find_if
,sort
,replace
, dll dan ini tidak akan terlihat begitu aneh lagi. Ini bisa menjadi kemenangan besar.Pembaruan 1:
Yang paling penting, ini membantu Anda melampaui
for_each
vs untuk-loop seperti itu semua yang ada, dan melihat STL-alogs lainnya, seperti menemukan / sort / partisi / copy_replace_if, eksekusi paralel .. atau apa pun.Banyak pemrosesan dapat ditulis dengan sangat ringkas menggunakan "sisa" saudara for_each, tetapi jika semua yang Anda lakukan adalah menulis for-loop dengan berbagai logika internal, maka Anda tidak akan pernah belajar cara menggunakannya, dan Anda akan akhirnya menciptakan roda berulang-ulang.
Dan (for_each gaya rentang yang akan segera tersedia):
Atau dengan C ++ x11 lambdas:
apakah IMO lebih mudah dibaca daripada:
Juga ini (atau dengan lambdas, lihat yang lain):
Lebih ringkas daripada:
Terutama jika Anda memiliki beberapa fungsi untuk dipanggil ... tapi mungkin itu hanya saya. ;)
Pembaruan 2 : Saya telah menulis pembungkus satu-liner saya sendiri dari stl-algos yang bekerja dengan rentang alih-alih sepasang iterator. boost :: range_ex, setelah dirilis, akan memasukkan itu dan mungkin itu akan ada di C ++ 0x juga?
sumber
outer_class::inner_class::iterator
atau mereka adalah argumen templat:typename std::vector<T>::iterator
... untuk konstruk itu sendiri dapat berjalan ke banyak garis konstruksinya sendirifor_each
contoh kedua tidak benar (seharusnyafor_each( bananas.begin(), bananas.end(),...
for_each
lebih generik. Anda dapat menggunakannya untuk beralih ke semua jenis wadah (dengan melewati iterator begin / end). Anda dapat berpotensi menukar wadah di bawah fungsi yang digunakanfor_each
tanpa harus memperbarui kode iterasi. Anda perlu mempertimbangkan bahwa ada wadah lain di dunia selainstd::vector
dan array C lama untuk melihat manfaatnyafor_each
.Kelemahan utama dari
for_each
ini adalah dibutuhkan functor, sehingga sintaksnya kikuk. Ini diperbaiki dalam C ++ 11 (sebelumnya C ++ 0x) dengan pengenalan lambdas:Ini tidak akan terlihat aneh bagi Anda dalam 3 tahun.
sumber
for ( int v : int_vector ) {
(bahkan jika itu dapat disimulasikan hari ini dengan BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Maksud saya mengapa seseorang dipaksa untuk menulis wadah dua kali?container.each { ... }
tanpa menyebutkan awal dan akhir iterator. Saya merasa sedikit berlebihan bahwa saya harus menentukan iterator akhir sepanjang waktu.Secara pribadi, setiap kali saya harus keluar dari cara saya untuk menggunakan
std::for_each
(menulis fungsi tujuan khusus / rumitboost::lambda
), saya menemukanBOOST_FOREACH
dan berbasis C ++ 0x untuk lebih jelas:vs.
sumber
sangat subyektif, beberapa orang akan mengatakan bahwa menggunakan
for_each
akan membuat kode lebih mudah dibaca, karena memungkinkan untuk memperlakukan koleksi yang berbeda dengan konvensi yang sama.for_each
itslef diimplementasikan sebagai loopjadi terserah kepada Anda untuk memilih apa yang tepat untuk Anda.
sumber
Seperti banyak fungsi algoritma, reaksi awal adalah berpikir bahwa lebih sulit untuk menggunakan foreach daripada loop. Sudah menjadi topik banyak perang api.
Setelah Anda terbiasa dengan idiom Anda mungkin merasa berguna. Satu keuntungan yang jelas adalah bahwa hal itu memaksa pembuat kode untuk memisahkan isi dalam loop dari fungsi iterasi yang sebenarnya. (OK, saya pikir ini keuntungan. Lainnya mengatakan Anda hanya memotong kode tanpa manfaat nyata).
Satu keuntungan lainnya adalah ketika saya melihat foreach, saya tahu bahwa setiap item akan diproses atau pengecualian akan dilemparkan.
A for loop memungkinkan beberapa opsi untuk mengakhiri loop. Anda dapat membiarkan loop menjalankan program penuhnya, atau Anda dapat menggunakan kata kunci break untuk secara eksplisit melompat keluar dari loop, atau menggunakan kata kunci kembali untuk keluar dari seluruh fungsi mid-loop. Sebaliknya, foreach tidak mengizinkan opsi ini, dan ini membuatnya lebih mudah dibaca. Anda bisa melihat sekilas pada nama fungsi dan Anda tahu sifat penuh iterasi.
Berikut ini adalah contoh dari membingungkan untuk lingkaran:
sumber
std::for_each()
dalam standar lama (pada saat posting ini) Anda harus menggunakan functor bernama, yang mendorong keterbacaan seperti yang Anda katakan dan melarang keluar dari loop sebelum waktunya. Tapi kemudianfor
loop ekivalen tidak memiliki apa-apa selain panggilan fungsi, dan itu juga melarang keluar sebelum waktunya. Tapi selain itu saya pikir Anda membuat poin yang sangat baik dalam mengatakan bahwastd::for_each()
memaksa melalui seluruh jajaran.Anda sebagian besar benar: sebagian besar waktu,
std::for_each
adalah kerugian bersih. Aku akan pergi sejauh untuk membandingkanfor_each
kegoto
.goto
memberikan kontrol aliran paling fleksibel yang memungkinkan - Anda dapat menggunakannya untuk mengimplementasikan hampir semua struktur kontrol lain yang dapat Anda bayangkan. Namun, keserbagunaan itu berarti bahwa melihat secaragoto
terpisah tidak memberi tahu Anda apa pun yang dimaksudkan untuk dilakukan dalam situasi ini. Akibatnya, hampir tidak ada orang waras yang menggunakangoto
kecuali sebagai pilihan terakhir.Di antara algoritma standar, cara
for_each
yang hampir sama - dapat digunakan untuk mengimplementasikan apa saja, yang berarti bahwa melihatfor_each
hampir tidak memberi tahu Anda tentang apa yang digunakan untuk situasi ini. Sayangnya, sikap orang terhadapfor_each
adalah tentang di mana sikap mereka terhadapgoto
(katakanlah) tahun 1970 atau lebih - beberapa orang telah menangkap fakta bahwa itu harus digunakan hanya sebagai jalan terakhir, tetapi banyak yang masih menganggapnya sebagai algoritma utama, dan jarang jika pernah menggunakan yang lain. Sebagian besar waktu, bahkan pandangan sekilas akan mengungkapkan bahwa salah satu alternatif secara drastis lebih unggul.Sebagai contoh, saya cukup yakin saya telah kehilangan jejak berapa kali saya melihat orang menulis kode untuk mencetak konten koleksi menggunakan
for_each
. Berdasarkan posting yang saya lihat, ini mungkin satu-satunya penggunaan yang paling umumfor_each
. Mereka berakhir dengan sesuatu seperti:Dan pasca mereka bertanya tentang apa kombinasi
bind1st
,mem_fun
, dll mereka perlu untuk membuat sesuatu seperti:bekerja, dan cetak elemen
coll
. Jika itu benar-benar bekerja persis seperti yang saya tulis di sana, itu akan biasa-biasa saja, tetapi tidak - dan pada saat Anda sudah membuatnya berfungsi, sulit untuk menemukan beberapa bit kode yang terkait dengan apa terjadi di antara potongan-potongan yang menyatukannya.Untungnya, ada cara yang jauh lebih baik. Tambahkan kelebihan inserter aliran normal untuk XXX:
dan gunakan
std::copy
:Itu bekerja - dan hampir tidak ada pekerjaan sama sekali untuk mengetahui bahwa ia mencetak konten
coll
untukstd::cout
.sumber
boost::mem_fn(&XXX::print)
bukanXXX::print
std::cout
sebagai argumennya agar itu berfungsi).Keuntungan menulis fungsional agar lebih mudah dibaca, mungkin tidak muncul kapan
for(...)
danfor_each(...
).Jika Anda menggunakan semua algoritma di functional.h, alih-alih menggunakan for-loop, kode menjadi jauh lebih mudah dibaca;
adalah jauh lebih mudah dibaca daripada;
Dan itulah yang menurut saya sangat bagus, menggeneralisasi for-loop ke fungsi satu baris =)
sumber
Mudah:
for_each
berguna ketika Anda sudah memiliki fungsi untuk menangani setiap item array, jadi Anda tidak perlu menulis lambda. Tentu saja inilebih baik dari
Juga,
for
loop berkisar hanya beralih di seluruh wadah dari awal sampai akhir, sementarafor_each
itu lebih fleksibel.sumber
The
for_each
Loop dimaksudkan untuk menyembunyikan iterator (detail dari bagaimana lingkaran diimplementasikan) dari kode pengguna dan mendefinisikan semantik yang jelas tentang operasi: setiap elemen akan mengulangi tepat sekali.Masalah dengan keterbacaan dalam standar saat ini adalah bahwa ia membutuhkan functor sebagai argumen terakhir alih-alih blok kode, jadi dalam banyak kasus Anda harus menulis jenis functor khusus untuk itu. Itu berubah menjadi kode yang kurang dapat dibaca sebagai objek functor tidak dapat didefinisikan di tempat (kelas lokal yang didefinisikan dalam fungsi tidak dapat digunakan sebagai argumen templat) dan implementasi loop harus dipindahkan dari loop aktual.
Perhatikan bahwa jika Anda ingin melakukan operasi tertentu pada setiap objek, Anda dapat menggunakan
std::mem_fn
, atauboost::bind
(std::bind
dalam standar berikutnya), atauboost::lambda
(lambda dalam standar berikutnya) untuk membuatnya lebih sederhana:Yang tidak lebih mudah dibaca dan lebih ringkas daripada versi linting tangan jika Anda memang memiliki fungsi / metode untuk memanggilnya. Implementasi dapat memberikan implementasi
for_each
loop lainnya (pikirkan pemrosesan paralel).Standar yang akan datang menangani beberapa kekurangan dengan cara yang berbeda, itu akan memungkinkan kelas yang didefinisikan secara lokal sebagai argumen untuk templat:
Meningkatkan lokalitas kode: ketika Anda menjelajah Anda melihat apa yang dilakukannya di sana. Faktanya, Anda bahkan tidak perlu menggunakan sintaks kelas untuk mendefinisikan functor, tetapi gunakan lambda di sana:
Bahkan jika untuk kasus
for_each
akan ada konstruksi spesifik yang akan membuatnya lebih alami:Saya cenderung mencampur
for_each
konstruk dengan loop linting tangan. Ketika hanya panggilan ke fungsi atau metode yang ada adalah apa yang saya butuhkan (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
) saya pergi untukfor_each
membangun yang mengambil dari kode banyak hal-hal iterator plat ketel. Ketika saya membutuhkan sesuatu yang lebih kompleks dan saya tidak dapat mengimplementasikan functor hanya beberapa baris di atas penggunaan aktual, saya menggulung loop saya sendiri (menjaga operasi tetap di tempat). Di bagian kode yang tidak kritis, saya mungkin menggunakan BOOST_FOREACH (rekan kerja memasukkan saya ke dalamnya)sumber
Selain keterbacaan dan kinerja, satu aspek yang sering diabaikan adalah konsistensi. Ada banyak cara untuk mengimplementasikan loop over iterator for (atau sementara), dari:
untuk:
dengan banyak contoh di antaranya pada berbagai tingkat efisiensi dan potensi bug.
Menggunakan for_each, bagaimanapun, menegakkan konsistensi dengan mengabstraksi loop:
Satu-satunya hal yang harus Anda khawatirkan sekarang adalah: apakah Anda mengimplementasikan loop body sebagai fungsi, functor, atau lambda menggunakan fitur Boost atau C ++ 0x? Secara pribadi, saya lebih suka khawatir tentang itu daripada bagaimana menerapkan atau membaca secara acak untuk / while.
sumber
Saya dulu tidak suka
std::for_each
dan berpikir bahwa tanpa lambda, itu dilakukan dengan sangat salah. Namun saya berubah pikiran beberapa waktu yang lalu, dan sekarang saya benar-benar menyukainya. Dan saya pikir itu bahkan meningkatkan keterbacaan, dan membuatnya lebih mudah untuk menguji kode Anda dengan cara TDD.The
std::for_each
algoritma dapat dibaca sebagai do sesuatu dengan semua elemen dalam jangkauan , yang dapat meningkatkan keterbacaan. Katakan tindakan yang ingin Anda lakukan sepanjang 20 baris, dan fungsi di mana tindakan dilakukan juga sekitar 20 baris. Itu akan membuat fungsi 40 baris panjang dengan loop konvensional, dan hanya sekitar 20 barisstd::for_each
, sehingga lebih mudah dipahami.Functors untuk
std::for_each
lebih cenderung lebih umum, dan dengan demikian dapat digunakan kembali, misalnya:Dan dalam kode Anda hanya akan memiliki satu-liner seperti
std::for_each(v.begin(), v.end(), DeleteElement())
yang sedikit lebih baik IMO daripada loop eksplisit.Semua functors itu biasanya lebih mudah untuk mendapatkan di bawah unit test daripada eksplisit untuk loop di tengah fungsi yang panjang, dan itu saja sudah merupakan kemenangan besar bagi saya.
std::for_each
juga umumnya lebih dapat diandalkan, karena Anda cenderung membuat kesalahan dengan jangkauan.Dan terakhir, kompiler mungkin menghasilkan kode yang sedikit lebih baik
std::for_each
daripada untuk jenis-jenis kerajinan tangan tertentu untuk loop, karena (for_each) selalu terlihat sama untuk kompiler, dan penulis kompiler dapat menaruh semua pengetahuan mereka, untuk membuatnya sebagus mereka bisa.Hal yang sama berlaku untuk algoritma std lain seperti
find_if
,transform
dll.sumber
for
adalah untuk loop yang dapat mengulangi setiap elemen atau setiap ketiga dllfor_each
adalah untuk hanya mengulangi setiap elemen. Jelas dari namanya. Jadi lebih jelas apa yang ingin Anda lakukan dalam kode Anda.sumber
++
. Mungkin tidak biasa, tetapi begitu juga untuk loop melakukan hal yang sama.transform
untuk tidak membingungkan seseorang.Jika Anda sering menggunakan algoritma lain dari STL, ada beberapa keuntungan untuk
for_each
:Tidak seperti tradisional untuk loop,
for_each
memaksa Anda untuk menulis kode yang akan bekerja untuk setiap iterator input. Dibatasi dengan cara ini sebenarnya bisa menjadi hal yang baik karena:for_each
.Menggunakan
for_each
kadang-kadang membuatnya lebih jelas bahwa Anda dapat menggunakan fungsi STL yang lebih spesifik untuk melakukan hal yang sama. (Seperti dalam contoh Jerry Coffin; itu belum tentu kasus yangfor_each
merupakan pilihan terbaik, tetapi untuk loop bukan satu-satunya alternatif.)sumber
Dengan C ++ 11 dan dua templat sederhana, Anda dapat menulis
sebagai pengganti
for_each
atau loop. Mengapa memilih itu bermuara pada singkatnya dan keamanan, tidak ada kemungkinan kesalahan dalam ekspresi yang tidak ada.Bagi saya,
for_each
selalu lebih baik dengan alasan yang sama ketika loop body sudah merupakan functor, dan saya akan mengambil keuntungan apa pun yang bisa saya dapatkan.Anda masih menggunakan tiga-ekspresi
for
, tetapi sekarang ketika Anda melihat satu Anda tahu ada sesuatu yang perlu dipahami di sana, itu bukan boilerplate. Aku benci piring. Saya membenci keberadaannya. Ini bukan kode nyata, tidak ada yang bisa dipelajari dengan membacanya, itu hanya satu hal lagi yang perlu diperiksa. Upaya mental dapat diukur dengan seberapa mudah berkarat dalam memeriksanya.Templatnya adalah
sumber
Sebagian besar Anda harus mengulang seluruh koleksi . Karena itu saya sarankan Anda menulis varian for_each () Anda sendiri, dengan hanya mengambil 2 parameter. Ini akan memungkinkan Anda untuk menulis ulang contoh Terry Mahaffey sebagai:
Saya pikir ini memang lebih mudah dibaca daripada for loop. Namun, ini membutuhkan ekstensi kompiler C ++ 0x.
sumber
Saya menemukan for_each buruk untuk keterbacaan. Konsepnya bagus, tetapi c ++ membuatnya sangat sulit untuk ditulis, setidaknya bagi saya. c ++ ekspresi 0x lamda akan membantu. Saya sangat menyukai gagasan para lama. Namun pada pandangan pertama saya pikir sintaksinya sangat jelek dan saya tidak 100% yakin saya akan pernah terbiasa dengannya. Mungkin dalam 5 tahun saya akan terbiasa dan tidak memikirkannya lagi, tapi mungkin tidak. Waktu akan menjawab :)
Saya lebih suka menggunakan
Saya menemukan eksplisit untuk loop lebih jelas untuk membaca dan explicity menggunakan variabel bernama untuk memulai dan mengakhiri iterators mengurangi kekacauan dalam for loop.
Tentu saja kasus bervariasi, ini hanya apa yang biasanya saya temukan yang terbaik.
sumber
Anda dapat membuat iterator menjadi panggilan ke fungsi yang dilakukan pada setiap iterasi melalui loop.
Lihat di sini: http://www.cplusplus.com/reference/algorithm/for_each/
sumber
for_each
dilakukannya, dalam hal ini, ia tidak menjawab pertanyaan tentang kelebihannya.Untuk loop bisa putus; Saya tidak ingin menjadi burung beo untuk Herb Sutter jadi di sini ada tautan ke presentasinya: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Pastikan untuk membaca komentar juga :)
sumber
for_each
memungkinkan kita untuk menerapkan pola Fork-Join . Selain itu mendukung antarmuka yang lancar .pola garpu-gabung
Kita dapat menambahkan implementasi
gpu::for_each
untuk menggunakan cuda / gpu untuk komputasi paralel-heterogen dengan memanggil tugas lambda di banyak pekerja.Dan
gpu::for_each
mungkin menunggu pekerja menyelesaikan semua tugas lambda sebelum menyelesaikan pernyataan berikutnya.antarmuka yang lancar
Itu memungkinkan kita untuk menulis kode yang bisa dibaca manusia dengan cara ringkas.
sumber