Keuntungan std :: for_each lebih dari untuk loop

166

Apakah ada kelebihan std::for_eachover forloop? Bagi saya, std::for_eachsepertinya hanya menghambat pembacaan kode. Mengapa beberapa standar pengkodean merekomendasikan penggunaannya?

missingfaktor
sumber
10
std::for_eachketika digunakan dengan boost.lambdaatau boost.bindsering dapat meningkatkan keterbacaan
Pertanyaan dan jawaban yang diterima berasal dari 2010. Untuk jawaban yang lebih mutakhir (dari 2018), lihat di sini: fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel Segal-Halevi

Jawaban:

181

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

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

Atau ini

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

ketika sintaks loop berbasis rentangfor tersedia:

for(Element& e : collection)
{
   foo(e);
}

Sintaks semacam ini telah tersedia di Java dan C # untuk beberapa waktu sekarang, dan sebenarnya ada lebih banyak foreachloop daripada forloop klasik di setiap kode Java atau C # yang saya lihat baru-baru ini.

Thomas Petit
sumber
12
Sebenarnya, foreach loop dengan scoop telah tersedia untuk waktu yang lama dalam meningkatkan, dan saya masih ingin beralih dengan for_each dan fungsi lambda.
Viktor Sehr
19
Asumsi tentang menginginkan seluruh rentang wadah bukan bagian dari pertanyaan, jadi ini hanya sebagian jawaban.
Adrian McCarthy
6
Perhatikan bahwa melompati elemen mungkin bukan satu-satunya hal yang ingin Anda lakukan, jadi itu bisa menjadi ide yang baik untuk menggunakan for_each hanya agar Anda belajar tentang find / partisi / copy_replace_if dan yang lainnya, yang merupakan jumlah sebenarnya untuk banyak loop melakukan.
Macke
10
Range-for itu bagus, kecuali ketika Anda benar-benar membutuhkan iterator (maka tidak ada cara untuk mendapatkannya).
Damon
5
Saya tidak akan menggunakan bahkan Element & esebagai auto & e(atau auto const &e) terlihat lebih baik. Saya akan menggunakan Element const e(tanpa referensi) ketika saya ingin konversi implisit, katakan ketika sumbernya adalah kumpulan dari berbagai jenis, dan saya ingin mereka mengubahnya menjadi Element.
Nawaz
53

Berikut beberapa alasannya:

  1. 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.)

  2. Hal ini memungkinkan Anda untuk menulis algoritma di atas for_each yang bekerja dengan iterator apa pun.

  3. Ini mengurangi kemungkinan bug pengetikan yang bodoh.

  4. 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_eachvs 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):

for_each(monsters, boost::mem_fn(&Monster::think));

Atau dengan C ++ x11 lambdas:

for_each(monsters, [](Monster& m) { m.think(); });

apakah IMO lebih mudah dibaca daripada:

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

Juga ini (atau dengan lambdas, lihat yang lain):

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

Lebih ringkas daripada:

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

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?

Macke
sumber
+1, beberapa fungsi atau tipe bersarang: outer_class::inner_class::iteratoratau mereka adalah argumen templat: typename std::vector<T>::iterator... untuk konstruk itu sendiri dapat berjalan ke banyak garis konstruksinya sendiri
David Rodríguez - dribeas
4
(btw: for_eachcontoh kedua tidak benar (seharusnyafor_each( bananas.begin(), bananas.end(),...
David Rodríguez - dribeas
Saya telah menulis pembungkus yang menggunakan rentang alih-alih dua iterator. Ini akan tersedia nanti (lihat range_ex) tetapi semua orang tetap harus memilikinya. (Ditambahkan pembaruan tentang itu.)
Macke
1
Dukungan pemrosesan paralel tidak. 1 alasan di sini. Kita dapat menambahkan implementasi untuk menggunakan cuda / gpu untuk komputasi paralel-heterogen.
shuva
24

for_eachlebih generik. Anda dapat menggunakannya untuk beralih ke semua jenis wadah (dengan melewati iterator begin / end). Anda dapat berpotensi menukar wadah di bawah fungsi yang digunakan for_eachtanpa harus memperbarui kode iterasi. Anda perlu mempertimbangkan bahwa ada wadah lain di dunia selain std::vectordan array C lama untuk melihat manfaatnya for_each.

Kelemahan utama dari for_eachini adalah dibutuhkan functor, sehingga sintaksnya kikuk. Ini diperbaiki dalam C ++ 11 (sebelumnya C ++ 0x) dengan pengenalan lambdas:

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

Ini tidak akan terlihat aneh bagi Anda dalam 3 tahun.

Terry Mahaffey
sumber
3
+1. Iklan ketika for_each mengambil wadah / rentang alih-alih dua iterator, itu lebih dahsyat.
Macke
2
@Marcus: itu akan menjadi konstruk rentang-untuk dan sintaks tidak akan membaca 'for_each' dengan sendirinya: for ( int v : int_vector ) {(bahkan jika itu dapat disimulasikan hari ini dengan BOOST_FOREACH)
David Rodríguez - dribeas
@ David: Saya merujuk pada penambahan umum fungsi berbasis rentang (sehingga Anda dapat menggunakan rentang dengan semua fungsi for_each ini, salin, remove_if, dll.),
Macke
1
Mengapa tidak mungkin untuk menulis: std::for_each(container, [](int& i){ ... });. Maksud saya mengapa seseorang dipaksa untuk menulis wadah dua kali?
Giorgio
1
@freitass: Menulis wadah sekali seperti dalam komentar saya sebelumnya dapat menggunakan default untuk menggunakan iterator mulai akhir tanpa memanggil mereka secara eksplisit. Sebagian besar bahasa yang menyediakan fungsi tingkat tinggi pada koleksi (Ruby, Scala, ...) menulis sesuatu container.each { ... }tanpa menyebutkan awal dan akhir iterator. Saya merasa sedikit berlebihan bahwa saya harus menentukan iterator akhir sepanjang waktu.
Giorgio
17

Secara pribadi, setiap kali saya harus keluar dari cara saya untuk menggunakan std::for_each(menulis fungsi tujuan khusus / rumit boost::lambda), saya menemukan BOOST_FOREACHdan berbasis C ++ 0x untuk lebih jelas:

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

vs.

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));
UncleBens
sumber
11

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_eachitslef diimplementasikan sebagai loop

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

jadi terserah kepada Anda untuk memilih apa yang tepat untuk Anda.

Alon
sumber
11

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:

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}
Andrew Shepherd
sumber
1
Anda membuat poin yang bagus, tetapi contoh motivasi Anda tidak sepenuhnya adil. Ketika Anda menggunakan 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 kemudian forloop 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 bahwa std::for_each() memaksa melalui seluruh jajaran.
wilhelmtell
10

Anda sebagian besar benar: sebagian besar waktu, std::for_eachadalah kerugian bersih. Aku akan pergi sejauh untuk membandingkan for_eachke goto. gotomemberikan 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 secara gototerpisah tidak memberi tahu Anda apa pun yang dimaksudkan untuk dilakukan dalam situasi ini. Akibatnya, hampir tidak ada orang waras yang menggunakan gotokecuali sebagai pilihan terakhir.

Di antara algoritma standar, cara for_eachyang hampir sama - dapat digunakan untuk mengimplementasikan apa saja, yang berarti bahwa melihat for_eachhampir tidak memberi tahu Anda tentang apa yang digunakan untuk situasi ini. Sayangnya, sikap orang terhadap for_eachadalah tentang di mana sikap mereka terhadap goto(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 umum for_each. Mereka berakhir dengan sesuatu seperti:

class XXX { 
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

Dan pasca mereka bertanya tentang apa kombinasi bind1st, mem_fun, dll mereka perlu untuk membuat sesuatu seperti:

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

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:

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}

dan gunakan std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

Itu bekerja - dan hampir tidak ada pekerjaan sama sekali untuk mengetahui bahwa ia mencetak konten colluntuk std::cout.

Jerry Coffin
sumber
+1, Ada satu kesalahan. Dalam contoh pertama itu seharusnya boost::mem_fn(&XXX::print)bukanXXX::print
missingfaktor
Itu sebabnya saya mengatakan bahwa contoh tidak akan berfungsi, dan mereka memposting meminta bantuan agar itu berfungsi (oh, dan Anda juga perlu mengikat std::coutsebagai argumennya agar itu berfungsi).
Jerry Coffin
1
Meskipun secara umum jawaban yang baik, pertanyaannya bukan tentang nilai for_each atau nilainya dibandingkan dengan algoritma std lainnya, tetapi tentang nilainya dibandingkan dengan for for loop. Anda mungkin memikirkannya jika tidak ada algoritma std lainnya yang berlaku. Apakah Anda kemudian menggunakan for_each atau for loop? Pikirkan tentang hal itu dan apa pun yang Anda pikirkan, itu seharusnya menjadi jawaban Anda.
Christian Rau
@ChristianRau: Selalu ada garis tipis antara menjawab pertanyaan persis seperti yang ditanyakan, dan mencoba memberikan informasi yang bermanfaat. Jawaban langsung untuk pertanyaan yang dia ajukan adalah "Mungkin tidak. Siapa yang tahu?", Tetapi akan terlalu sia-sia untuk menjadi masalah. Pada saat yang sama terlalu jauh (misalnya, merekomendasikan Haskell dan bukan yang di atas) juga tidak akan banyak berguna.
Jerry Coffin
3
@ChristianRau: Bagaimana menurut Anda bahwa "... sebagian besar waktu, std :: for_each adalah kerugian bersih" tidak menjawab pertanyaan apakah std :: for_each memberikan keuntungan?
Jerry Coffin
8

Keuntungan menulis fungsional agar lebih mudah dibaca, mungkin tidak muncul kapan for(...)dan for_each(...).

Jika Anda menggunakan semua algoritma di functional.h, alih-alih menggunakan for-loop, kode menjadi jauh lebih mudah dibaca;

iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);

adalah jauh lebih mudah dibaca daripada;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it > *longest_tree) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

Dan itulah yang menurut saya sangat bagus, menggeneralisasi for-loop ke fungsi satu baris =)

Viktor Sehr
sumber
6

Mudah: for_eachberguna ketika Anda sudah memiliki fungsi untuk menangani setiap item array, jadi Anda tidak perlu menulis lambda. Tentu saja ini

for_each(a.begin(), a.end(), a_item_handler);

lebih baik dari

for(auto& item: a) {
    a_item_handler(a);
}

Juga, forloop berkisar hanya beralih di seluruh wadah dari awal sampai akhir, sementara for_eachitu lebih fleksibel.

Tigran Saluev
sumber
4

The for_eachLoop 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.

struct myfunctor {
   void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), myfunctor() );
   // more code
}

Perhatikan bahwa jika Anda ingin melakukan operasi tertentu pada setiap objek, Anda dapat menggunakan std::mem_fn, atau boost::bind( std::binddalam standar berikutnya), atau boost::lambda(lambda dalam standar berikutnya) untuk membuatnya lebih sederhana:

void function( int value );
void apply( std::vector<X> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
   // code
}

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_eachloop 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:

void apply( std::vector<int> const & v ) {
   // code
   struct myfunctor {
      void operator()( int ) { code }
   };
   std::for_each( v.begin(), v.end(), myfunctor() );
   // code
}

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:

void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), 
      []( int ) { // code } );
   // code
}

Bahkan jika untuk kasus for_eachakan ada konstruksi spesifik yang akan membuatnya lebih alami:

void apply( std::vector<int> const & v ) {
   // code
   for ( int i : v ) {
      // code
   }
   // code
}

Saya cenderung mencampur for_eachkonstruk 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 untuk for_eachmembangun 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)

David Rodríguez - dribeas
sumber
3

Selain keterbacaan dan kinerja, satu aspek yang sering diabaikan adalah konsistensi. Ada banyak cara untuk mengimplementasikan loop over iterator for (atau sementara), dari:

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

untuk:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

dengan banyak contoh di antaranya pada berbagai tingkat efisiensi dan potensi bug.

Menggunakan for_each, bagaimanapun, menegakkan konsistensi dengan mengabstraksi loop:

for_each(c.begin(), c.end(), do_something);

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.

Jason Govig
sumber
3

Saya dulu tidak suka std::for_eachdan 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_eachalgoritma 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 baris std::for_each, sehingga lebih mudah dipahami.

Functors untuk std::for_eachlebih cenderung lebih umum, dan dengan demikian dapat digunakan kembali, misalnya:

struct DeleteElement
{
    template <typename T>
    void operator()(const T *ptr)
    {
        delete ptr;
    }
};

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_eachdaripada 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, transformdll.

Dmitry
sumber
2

foradalah untuk loop yang dapat mengulangi setiap elemen atau setiap ketiga dll for_eachadalah untuk hanya mengulangi setiap elemen. Jelas dari namanya. Jadi lebih jelas apa yang ingin Anda lakukan dalam kode Anda.

Kirill V. Lyadvinsky
sumber
4
tidak jika Anda memberikan iterator yang maju dengan 3 masing-masing ++. Mungkin tidak biasa, tetapi begitu juga untuk loop melakukan hal yang sama.
Potatoswatter
Dalam hal ini mungkin lebih baik digunakan transformuntuk tidak membingungkan seseorang.
Kirill V. Lyadvinsky
2

Jika Anda sering menggunakan algoritma lain dari STL, ada beberapa keuntungan untuk for_each:

  1. Seringkali akan lebih sederhana dan lebih rentan kesalahan daripada for for loop, sebagian karena Anda akan terbiasa dengan fungsi-fungsi dengan antarmuka ini, dan sebagian karena sebenarnya sedikit lebih ringkas dalam banyak kasus.
  2. Meskipun range-based untuk loop bahkan bisa lebih sederhana, itu kurang fleksibel (seperti dicatat oleh Adrian McCarthy, iterates di atas seluruh wadah).
  3. Tidak seperti tradisional untuk loop, for_eachmemaksa Anda untuk menulis kode yang akan bekerja untuk setiap iterator input. Dibatasi dengan cara ini sebenarnya bisa menjadi hal yang baik karena:

    1. Anda mungkin perlu mengadaptasi kode untuk bekerja pada wadah yang berbeda nanti.
    2. Pada awalnya, ini mungkin mengajarkan Anda sesuatu dan / atau mengubah kebiasaan Anda menjadi lebih baik.
    3. Bahkan jika Anda akan selalu menulis untuk loop yang setara dengan sempurna, orang lain yang memodifikasi kode yang sama mungkin tidak melakukan ini tanpa diminta untuk menggunakannya for_each.
  4. Menggunakan for_eachkadang-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 yang for_eachmerupakan pilihan terbaik, tetapi untuk loop bukan satu-satunya alternatif.)

Sean Patrick Santos
sumber
2

Dengan C ++ 11 dan dua templat sederhana, Anda dapat menulis

        for ( auto x: range(v1+4,v1+6) ) {
                x*=2;
                cout<< x <<' ';
        }

sebagai pengganti for_eachatau loop. Mengapa memilih itu bermuara pada singkatnya dan keamanan, tidak ada kemungkinan kesalahan dalam ekspresi yang tidak ada.

Bagi saya, for_eachselalu 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

template<typename iter>
struct range_ { 
                iter begin() {return __beg;}    iter end(){return __end;}
            range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
            iter __beg, __end;
};

template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
    { return range_<iter>(begin,end); }
jthill
sumber
1

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:

for_each(container, [](int& i) {
    i += 10;
});

Saya pikir ini memang lebih mudah dibaca daripada for loop. Namun, ini membutuhkan ekstensi kompiler C ++ 0x.

Dimitri C.
sumber
1

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

vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
  // Do stuff
}

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.

jcoder
sumber
0

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/

sdornan
sumber
2
Posting hanya tautan tidak membuat jawaban yang baik, dan lagi pula, di mana dalam tautan itu ditampilkan sesuatu yang menyerupai iterator yang dapat dipanggil? Saya cukup yakin bahwa konsep itu tidak masuk akal. Mungkin Anda hanya meringkas apa yang for_eachdilakukannya, dalam hal ini, ia tidak menjawab pertanyaan tentang kelebihannya.
underscore_d
0

for_eachmemungkinkan kita untuk menerapkan pola Fork-Join . Selain itu mendukung antarmuka yang lancar .

pola garpu-gabung

Kita dapat menambahkan implementasi gpu::for_eachuntuk menggunakan cuda / gpu untuk komputasi paralel-heterogen dengan memanggil tugas lambda di banyak pekerja.

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

Dan gpu::for_eachmungkin 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.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
shuva
sumber