Bagaimana cara membuat variabel loop for const dengan pengecualian pernyataan kenaikan?

Pertimbangkan loop for standar:

for (int i = 0; i < 10; ++i) 
{
   // do something with i
}

Saya ingin mencegah variabel i diubah di badan loop for.

Namun, saya tidak dapat mendeklarasikan i sebagai const karena hal ini membuat pernyataan kenaikan menjadi tidak valid. Apakah ada cara untuk menjadikan i menjadi variabel const di luar pernyataan kenaikan?


person jhourback    schedule 13.08.2020    source sumber
comment
Saya yakin tidak ada cara untuk melakukan ini   -  person Itay    schedule 13.08.2020
comment
Anda perlu menyembunyikan variabel dari badan loop, mungkin mengubahnya menjadi sesuatu seperti while(i_copy = loop()) { }   -  person stark    schedule 13.08.2020
comment
Anda dapat membuat referensi konstan ke dalamnya di badan perulangan, mis. const int& i_safe = i. Kompiler Anda harus menghindari tipuan apa pun.   -  person Brian    schedule 13.08.2020
comment
@Brian Itu masih membuat i rentan terhadap badan loop jahat.   -  person stark    schedule 13.08.2020
comment
Saya berasumsi solusi untuk menghilangkan i tidak baik? Anda ingin memiliki akses hanya baca ke i di loop?   -  person cigien    schedule 13.08.2020
comment
Saya selalu ingin mengusulkan for({int i=0; i<10; ++i}){ untuk hal ini, tetapi tidak pernah memiliki keberanian untuk menyarankannya kepada komite standar, atau kepada Bjarne ketika dia melakukan pekerjaan jujurnya sehari-hari di Morgan Stanley. Tentu saja saran saya tidak mengizinkan Anda mengakses i di badan.   -  person Bathsheba    schedule 13.08.2020
comment
Jika Anda tetap diam, tidak akan ada yang berubah. Kecuali dia akan mencambuk Anda dan mengeluarkan Anda dari universitas karena memberikan saran yang baik (jika demikian, apa yang Anda lakukan di sana?), maka tidak ada yang berani, tidak ada keuntungan.   -  person Michael Dorgan    schedule 13.08.2020
comment
Ini terdengar seperti solusi untuk mencari suatu masalah.   -  person Pete Becker    schedule 13.08.2020
comment
Ubah isi for-loop Anda menjadi fungsi dengan argumen const int i. Mutabilitas indeks hanya diekspos jika diperlukan dan Anda dapat menggunakan kata kunci inline untuk membuatnya tidak berpengaruh pada keluaran yang dikompilasi.   -  person Monty Thibault    schedule 14.08.2020
comment
Apa (atau lebih tepatnya, siapa) yang mungkin dapat mengubah nilai indeks selain.... Anda? Apakah Anda tidak mempercayai diri sendiri? Mungkin rekan kerja? Saya setuju dengan @PeteBecker.   -  person Z4-tier    schedule 14.08.2020
comment
@ Z4-tier Ya, tentu saja saya tidak percaya diri. Saya tahu bahwa saya melakukan kesalahan. Setiap programmer yang baik tahu. Itu sebabnya kami memiliki hal-hal seperti const untuk memulai.   -  person Konrad Rudolph    schedule 16.08.2020
comment
Jika Anda ingin menghindari kesalahan, maka Anda tidak boleh menggunakan perulangan for yang diindeks sama sekali karena perulangan tersebut dapat mengalami kesalahan satu kali saja.   -  person Phil1970    schedule 17.08.2020
comment
Dalam praktiknya, hal ini seharusnya tidak menjadi masalah. Jika loop Anda kecil, maka mudah untuk melihat bahwa ì diubah dalam loop. Jika loop Anda besar, maka loop tersebut harus difaktorkan ulang menjadi sebuah fungsi.   -  person Phil1970    schedule 17.08.2020


Jawaban (9)


Dari c++20, Anda dapat menggunakan ranges::views::iota seperti ini:

for (int const i : std::views::iota(0, 10))
{
   std::cout << i << " ";  // ok
   i = 42;                 // error
}

Berikut demo.


Dari c++11, Anda juga dapat menggunakan teknik berikut, yang menggunakan IIILE (ekspresi lambda inline yang segera dipanggil):

int x = 0;
for (int i = 0; i < 10; ++i) [&,i] {
    std::cout << i << " ";  // ok, i is readable
    i = 42;                 // error, i is captured by non-mutable copy
    x++;                    // ok, x is captured by mutable reference
}();     // IIILE

Berikut demo.

Perhatikan bahwa [&,i] berarti i ditangkap oleh salinan yang tidak dapat diubah, dan segala sesuatu yang lain ditangkap oleh referensi yang dapat diubah. Tanda (); di akhir perulangan berarti lambda segera dipanggil.

person cigien    schedule 13.08.2020
comment
Hampir memerlukan konstruksi loop for khusus karena yang ditawarkannya adalah alternatif yang lebih aman daripada konstruksi yang sangat, sangat umum. - person Michael Dorgan; 13.08.2020
comment
@MichaelDorgan Nah, karena sudah ada dukungan perpustakaan untuk fitur ini, tidak ada gunanya menambahkannya sebagai fitur bahasa inti. - person cigien; 14.08.2020
comment
Adil, meski hampir semua pekerjaan saya yang sebenarnya paling banyak masih C atau C++11. Aku belajar kalau-kalau itu penting di masa depan bagiku... - person Michael Dorgan; 14.08.2020
comment
Trik C++11 yang Anda tambahkan dengan lambda sudah rapi, tetapi tidak akan praktis di sebagian besar tempat kerja yang pernah saya ikuti. Analisis statis akan mengeluhkan penangkapan & yang digeneralisasi, yang akan memaksa menangkap setiap referensi secara eksplisit -- yang membuat ini cukup rumit. Saya juga curiga bahwa hal ini dapat menyebabkan bug yang mudah terjadi ketika penulis lupa (), sehingga kode tidak pernah dipanggil. Ini cukup kecil untuk dilewatkan dalam peninjauan kode juga. - person Human-Compiler; 14.08.2020
comment
apakah ini IIILE jika tidak menginisialisasi apa pun? pasti terlalu banyak 'aku' - person Pete Kirkham; 14.08.2020
comment
Meskipun secara teknis, lambda adalah solusi yang bagus untuk masalah ini, dalam praktiknya Anda memiliki lebih banyak peluang untuk melakukan kesalahan di dalamnya daripada kesalahan yang tidak mungkin terjadi saat memodifikasi i secara tidak sengaja. Dan kodenya lebih sulit dipahami oleh sebagian besar pembaca. - person Phil1970; 17.08.2020
comment
@ Human-Compiler Saya tidak yakin saya melihat masalah yang Anda tunjukkan. dentang setidaknya akan memperingatkan jika Anda melewatkan (). Peringatan analisis statis juga tampaknya salah; badan perulangan dalam kode OP harus memiliki akses ke persis variabel yang sama sebagai hasil penangkapan & di lambda. - person cigien; 17.08.2020
comment
@ Phil1970 Saya memahami masalah ini karena sebagian besar pembaca tidak menyadari idiom tersebut, tetapi kesalahan apa sebenarnya yang Anda maksud? Bisakah Anda menunjukkan beberapa potensi jebakan? - person cigien; 17.08.2020
comment
@cigien Alat analisis statis seperti SonarQube dan cppcheck menandai tangkapan umum seperti [&] karena bertentangan dengan standar pengkodean seperti AUTOSAR (Peraturan A5-1-2), HIC++, dan menurut saya juga MISRA (tidak yakin). Bukan berarti hal itu tidak benar; organisasi melarang jenis kode ini agar sesuai dengan standar. Sedangkan untuk (), versi gcc terbaru tidak menandai ini bahkan dengan -Wextra. Menurut saya pendekatannya masih rapi; itu tidak berhasil di banyak organisasi. - person Human-Compiler; 18.08.2020
comment
Saya tahu aturan SonarQube yang saya tautkan menyatakan bahwa tidak ada salahnya jika digunakan segera; namun dalam praktiknya saya melihatnya menandai penggunaan [&] setiap di lingkungan yang berbeda. Mungkin saja hal ini disebabkan oleh aturan tambahan yang disiapkan untuk kepatuhan standar pengkodean yang saya sebutkan di atas. Juga fakta bahwa gcc tidak memperingatkan ketika tidak segera memanggil lambda menurut saya adalah salah satu potensi kejatuhan yang mungkin dirujuk oleh @ Phil1970 - person Human-Compiler; 18.08.2020

Bagi siapa pun yang menyukai jawaban std::views::iota Cigien tetapi tidak bekerja di C++20 atau lebih tinggi, cukup mudah untuk mengimplementasikan versi sederhana dan ringan dari kelas std::views::iota ="post-tag" title="tampilkan pertanyaan yang diberi tag 'c++11'" rel="tag">c++11 atau lebih tinggi.

Yang diperlukan hanyalah:

  • Tipe dasar LegacyInputIterator (sesuatu yang mendefinisikan operator++ dan operator*) yang membungkus nilai integral (misalnya int)
  • Beberapa kelas seperti rentang yang memiliki begin() dan end() yang mengembalikan iterator di atas. Ini akan memungkinkannya bekerja dalam loop for berbasis rentang

Versi sederhananya bisa berupa:

#include <iterator>

// This is just a class that wraps an 'int' in an iterator abstraction
// Comparisons compare the underlying value, and 'operator++' just
// increments the underlying int
class counting_iterator
{
public:
    // basic iterator boilerplate
    using iterator_category = std::input_iterator_tag;
    using value_type = int;
    using reference  = int;
    using pointer    = int*;
    using difference_type = std::ptrdiff_t;

    // Constructor / assignment
    constexpr explicit counting_iterator(int x) : m_value{x}{}
    constexpr counting_iterator(const counting_iterator&) = default;
    constexpr counting_iterator& operator=(const counting_iterator&) = default;

    // "Dereference" (just returns the underlying value)
    constexpr reference operator*() const { return m_value; }
    constexpr pointer operator->() const { return &m_value; }

    // Advancing iterator (just increments the value)
    constexpr counting_iterator& operator++() {
        m_value++;
        return (*this);
    }
    constexpr counting_iterator operator++(int) {
        const auto copy = (*this);
        ++(*this);
        return copy;
    }

    // Comparison
    constexpr bool operator==(const counting_iterator& other) const noexcept {
        return m_value == other.m_value;
    }
    constexpr bool operator!=(const counting_iterator& other) const noexcept {
        return m_value != other.m_value;
    }
private:
    int m_value;
};

// Just a holder type that defines 'begin' and 'end' for
// range-based iteration. This holds the first and last element
// (start and end of the range)
// The begin iterator is made from the first value, and the
// end iterator is made from the second value.
struct iota_range
{
    int first;
    int last;
    constexpr counting_iterator begin() const { return counting_iterator{first}; }
    constexpr counting_iterator end() const { return counting_iterator{last}; }
};

// A simple helper function to return the range
// This function isn't strictly necessary, you could just construct
// the 'iota_range' directly
constexpr iota_range iota(int first, int last)
{
    return iota_range{first, last};
}

Saya telah mendefinisikan hal di atas dengan constexpr jika didukung, tetapi untuk versi C++ sebelumnya seperti C++11/14, Anda mungkin perlu menghapus constexpr jika versi tersebut tidak sah untuk melakukannya.

Boilerplate di atas memungkinkan kode berikut berfungsi di pra-C++20:

for (int const i : iota(0, 10))
{
   std::cout << i << " ";  // ok
   i = 42;                 // error
}

Yang akan menghasilkan perakitan yang sama dengan solusi C++20 std::views::iota dan solusi for-loop klasik ketika dioptimalkan.

Ini berfungsi dengan semua kompiler yang mendukung C++11 (misalnya kompiler seperti gcc-4.9.4) dan masih menghasilkan perakitan yang hampir identik ke rekanan for-loop dasar.

Catatan: Fungsi pembantu iota hanya untuk paritas fitur dengan solusi C++20 std::views::iota; namun secara realistis, Anda juga dapat langsung membuat iota_range{...} alih-alih memanggil iota(...). Yang pertama hanya menyajikan jalur peningkatan yang mudah jika pengguna ingin beralih ke C++20 di masa mendatang.

person Human-Compiler    schedule 13.08.2020
comment
Ini memerlukan sedikit boilerplate, tapi sebenarnya tidak terlalu rumit dalam hal apa yang dilakukannya. Ini sebenarnya hanya pola iterator dasar, tetapi membungkus int, lalu membuat kelas rentang untuk mengembalikan awal/akhir - person Human-Compiler; 14.08.2020
comment
Tidak terlalu penting, tetapi saya juga menambahkan solusi c++11 yang tidak diposting orang lain, jadi Anda mungkin ingin sedikit menulis ulang baris pertama jawaban Anda :) - person cigien; 14.08.2020
comment
Saya tidak yakin siapa yang memberikan suara negatif, tetapi saya akan menghargai masukan jika Anda merasa jawaban saya tidak memuaskan sehingga saya dapat memperbaikinya. Memberi suara negatif adalah cara yang bagus untuk menunjukkan bahwa Anda merasa suatu jawaban tidak cukup menjawab pertanyaan, namun dalam kasus ini tidak ada kritik atau kesalahan nyata dalam jawaban yang dapat saya perbaiki. - person Human-Compiler; 14.08.2020
comment
@ Human-Compiler Saya juga mendapat DV pada saat yang sama, dan mereka juga tidak mengomentari alasannya :( Sepertinya seseorang tidak menyukai abstraksi rentang. Saya tidak akan mengkhawatirkannya. - person cigien; 14.08.2020
comment
perakitan adalah kata benda massal seperti bagasi atau air. Ungkapan normalnya adalah will dikompilasi ke assembly yang sama dengan C++20 .... Output asm kompiler untuk satu fungsi bukanlah a assembly tunggal, itu perakitan (urutan instruksi bahasa perakitan). - person Peter Cordes; 14.08.2020
comment
@PeterCordes - Sama seperti bentuk jamak dari air adalah perairan, bentuk jamak dari majelis adalah majelis. Kata assembly yang digunakan di sini biasanya dipandang sebagai singkatan dari pengajaran bahasa assembly, dalam hal ini mungkin assemblies akan lebih baik jika berbentuk jamak. Namun dapat juga dipandang sebagai singkatan dari kumpulan (alias rakitan) instruksi bahasa rakitan, dalam hal ini istilah yang benar adalah rakitan. - person David Hammen; 15.08.2020
comment
@ Human-Compiler - Suara negatif drive-by acak untuk jawaban yang baik tanpa komentar untuk suara negatif adalah kejadian biasa di seluruh jaringan StackExchange. - person David Hammen; 15.08.2020
comment
Satu-satunya kritik saya adalah bahwa fungsi iota bertentangan dengan templat fungsi c++11 std::iota, yang melakukan sesuatu yang sangat berbeda. Solusinya adalah dengan menambahkan konstruktor ke kelas iota_range Anda. Meskipun ini bertentangan dengan c++20 std::iota_range, iota_range dan c++20 std::iota_range Anda mencapai hal yang hampir sama. - person David Hammen; 15.08.2020
comment
@DavidHammen: Benar, ada bentuk kata benda yang dapat dihitung dari kata assembly dalam bahasa Inggris, untuk mendeskripsikan kumpulan benda. Dalam komputasi, Anda dapat memiliki rakitan .NET; perhatikan artikel tak tentu a di sana; Anda dapat memiliki beberapa rakitan .NET. Namun dalam konteks keluaran bahasa rakitan dari kompiler, ini adalah kata benda massal, bukan tunggal. Pertimbangkan kompiler C++ yang dikompilasi ke C: jika Anda berbicara tentang outputnya, Anda mungkin mengatakan kedua sumber C++ ini menghasilkan C yang sama, bukan C yang sama. Bahasa rakitan didasarkan pada bentuk yang tidak dapat dihitung. - person Peter Cordes; 15.08.2020
comment
@PeterCordes Saya menggunakan istilah rakitan dalam kasus ini karena saya merujuk keluaran kode rakitan dari dua sumber berbeda; C++20 yang dioptimalkan std::views::Iota dan loop for idiomatis. Saya pikir dalam kasus ini Majelis yang sama atau majelis yang sama keduanya benar secara leksikal, meskipun setelah membacanya kembali, saya dapat melihat mengapa kedengarannya tidak tepat. - person Human-Compiler; 16.08.2020
comment
@DavidHammen Saya setuju dengan komentar yang iota Anda bertentangan, tetapi std::views::iota juga bertentangan dengan std::iota. Saya kebanyakan hanya menambahkan ini untuk paritas dengan padanan C++20 -- tetapi sebaliknya saya sepenuhnya setuju bahwa fungsinya sepenuhnya dangkal dan dapat dilakukan hanya dengan rentang. Saya pikir lebih mudah untuk menyajikan solusi dengan karakteristik yang identik dengan jawaban standar yang sudah ada, karena solusi ini menyajikan solusi peningkatan yang lebih jelas jika suatu proyek mengadopsi standar yang lebih baru. - person Human-Compiler; 16.08.2020

Versi CIUMAN...

for (int _i = 0; _i < 10; ++_i) {
    const int i = _i;

    // use i here
}

Jika kasus penggunaan Anda hanya untuk mencegah modifikasi indeks loop yang tidak disengaja, maka ini akan membuat bug tersebut terlihat jelas. (Jika Anda ingin mencegah modifikasi yang disengaja, semoga berhasil...)

person Artelius    schedule 14.08.2020
comment
Saya pikir Anda memberikan pelajaran yang salah dengan menggunakan pengidentifikasi ajaib yang dimulai dengan _. Dan sedikit penjelasan (misalnya ruang lingkup) akan sangat membantu. Kalau tidak, ya, baiklah KISSy. - person Yunnosch; 14.08.2020
comment
Memanggil variabel tersembunyi i_ akan lebih sesuai. - person Yirkha; 14.08.2020
comment
Saya tidak yakin bagaimana ini menjawab pertanyaan itu. Variabel loop adalah _i yang masih dapat dimodifikasi dalam loop. - person cigien; 14.08.2020
comment
@cigien: IMO, solusi parsial ini layak dilakukan tanpa C++20 std::views::iota untuk cara yang sepenuhnya antipeluru. Teks jawabannya menjelaskan keterbatasannya dan bagaimana ia mencoba menjawab pertanyaan tersebut. Sekelompok C++11 yang terlalu rumit membuat penyembuhannya lebih buruk daripada penyakitnya dalam hal IMO yang mudah dibaca dan dipelihara. Ini masih sangat mudah dibaca oleh semua orang yang mengetahui C++, dan tampaknya masuk akal sebagai sebuah idiom. (Tetapi sebaiknya hindari nama yang diberi garis bawah di depan.) - person Peter Cordes; 14.08.2020
comment
@Yunnosch hanya pengidentifikasi _Uppercase dan double__underscore yang dicadangkan. _lowercase pengidentifikasi hanya dicadangkan dalam lingkup global. - person Roman Odaisky; 14.08.2020

Tidak bisakah Anda memindahkan sebagian atau seluruh konten perulangan for Anda ke dalam fungsi yang menerima i sebagai konstanta?

Ini kurang optimal dibandingkan beberapa solusi yang diusulkan, namun jika memungkinkan hal ini cukup mudah dilakukan.

Sunting: Sekadar contoh karena saya cenderung tidak jelas.

for (int i = 0; i < 10; ++i) 
{
   looper( i );
}

void looper ( const int v )
{
    // do your thing here
}
person Al rl    schedule 15.08.2020

Jika Anda tidak memiliki akses ke c ++20, perubahan tipikal menggunakan suatu fungsi

#include <vector>
#include <numeric> // std::iota

std::vector<int> makeRange(const int start, const int end) noexcept
{
   std::vector<int> vecRange(end - start);
   std::iota(vecRange.begin(), vecRange.end(), start);
   return vecRange;
}

sekarang kamu bisa

for (const int i : makeRange(0, 10))
{
   std::cout << i << " ";  // ok
   //i = 100;              // error
}

(Lihat Demo)


Pembaruan: Terinspirasi dari komentar @Human-Compiler, saya bertanya-tanya apakah jawaban yang diberikan memiliki perbedaan dalam hal kinerja. Ternyata, kecuali pendekatan ini, secara mengejutkan semua pendekatan lainnya memiliki kinerja yang sama (untuk rentang [0, 10)). Pendekatan std::vector adalah yang terburuk.

masukkan deskripsi gambar di sini

(Lihat Quick-Bench Online)

person JeJo    schedule 13.08.2020
comment
Meskipun ini berfungsi untuk pra-c++20, ini memiliki jumlah overhead yang cukup besar karena memerlukan penggunaan vector. Jika rentangnya sangat besar, ini bisa berdampak buruk. - person Human-Compiler; 13.08.2020
comment
@Human-Compiler: A std::vector cukup buruk pada skala relatif jika rentangnya juga kecil, dan bisa menjadi sangat buruk jika ini seharusnya merupakan loop dalam kecil yang dijalankan berkali-kali. Beberapa kompiler (seperti dentang dengan libc++, tetapi tidak libstdc++) dapat mengoptimalkan baru/menghapus alokasi yang tidak lolos dari fungsi, tetapi jika tidak, ini bisa dengan mudah menjadi perbedaan antara loop kecil yang belum dibuka sepenuhnya vs. panggilan ke new + delete, dan mungkin benar-benar menyimpannya ke dalam memori itu. - person Peter Cordes; 14.08.2020
comment
IMO, manfaat kecil dari const i tidak sebanding dengan biaya overhead untuk sebagian besar kasus, tanpa cara C++20 yang membuatnya murah. Terutama dengan rentang variabel runtime yang memperkecil kemungkinan kompiler untuk mengoptimalkan semuanya. - person Peter Cordes; 14.08.2020

Dan ini adalah versi C++11:

for (int const i : {0,1,2,3,4,5,6,7,8,9,10})
{
    std::cout << i << " ";
    // i = 42; // error
}

Berikut adalah demo langsung

person Vlad Feinstein    schedule 13.08.2020
comment
Ini tidak berskala jika angka maksimal ditentukan oleh nilai runtime. - person Human-Compiler; 13.08.2020
comment
@ Human-Compiler Cukup perluas daftar ke nilai yang diinginkan dan kompilasi ulang seluruh program Anda secara dinamis;) - person Monty Thibault; 14.08.2020
comment
Anda tidak menyebutkan kasus {..}. Anda perlu memasukkan sesuatu untuk mengaktifkan fitur ini. Misalnya, kode Anda akan rusak jika Anda tidak menambahkan header yang tepat: godbolt.org/z/esbhra . Menyampaikan <iostream> untuk header lainnya adalah ide yang buruk! - person JeJo; 14.08.2020

#include <cstdio>
  
#define protect(var) \
  auto &var ## _ref = var; \
  const auto &var = var ## _ref

int main()
{
  for (int i = 0; i < 10; ++i) 
  {
    {
      protect(i);
      // do something with i
      //
      printf("%d\n", i);
      i = 42; // error!! remove this and it compiles.
    }
  }
}

Catatan: kita perlu menyarangkan ruang lingkup karena kebodohan yang menakjubkan dalam bahasa ini: variabel yang dideklarasikan di header for(...) dianggap berada pada level bersarang yang sama dengan variabel yang dideklarasikan dalam pernyataan gabungan {...}. Artinya, misalnya:

for (int i = ...)
{
  int i = 42; // error: i redeclared in same scope
}

Apa? Bukankah kita baru saja membuka kurung kurawal? Selain itu, ini tidak konsisten:

void fun(int i)
{
  int i = 42; // OK
}
person Kaz    schedule 14.08.2020
comment
Ini adalah jawaban terbaik dengan mudah. Memanfaatkan 'variable shadowing' C++ untuk menyebabkan pengidentifikasi memutuskan ke variabel const ref yang merujuk pada variabel langkah asli, adalah solusi yang elegan. Atau setidaknya, yang paling elegan yang pernah ada. - person Max Barraclough; 15.08.2020

Salah satu pendekatan sederhana yang belum disebutkan di sini yang berfungsi di versi C++ mana pun adalah membuat pembungkus fungsional di sekitar suatu rentang, mirip dengan apa yang std::for_each lakukan pada iterator. Pengguna kemudian bertanggung jawab untuk meneruskan argumen fungsional sebagai panggilan balik yang akan dipanggil pada setiap iterasi.

Misalnya:

// A struct that holds the start and end value of the range
struct numeric_range
{
    int start;
    int end;

    // A simple function that wraps the 'for loop' and calls the function back
    template <typename Fn>
    void for_each(const Fn& fn) const {
        for (auto i = start; i < end; ++i) {
            const auto& const_i = i;
            fn(const_i);
        }
    }
};

Dimana kegunaannya adalah:

numeric_range{0, 10}.for_each([](const auto& i){
   std::cout << i << " ";  // ok
   //i = 100;              // error
});

Apa pun yang lebih tua dari C++11 akan terjebak meneruskan penunjuk fungsi yang diberi nama kuat ke for_each (mirip dengan std::for_each), tetapi masih berfungsi.

Berikut demo


Meskipun ini mungkin bukan idiomatis untuk for loop di C++, pendekatan ini cukup umum di bahasa lain. Pembungkus fungsional sangat ramping karena komposisinya dalam pernyataan yang kompleks dan bisa sangat ergonomis untuk digunakan.

Kode ini juga mudah untuk ditulis, dipahami, dan dipelihara.

person Human-Compiler    schedule 20.08.2020
comment
Salah satu batasan yang harus diperhatikan dalam pendekatan ini adalah bahwa beberapa organisasi melarang pengambilan default pada lambda (misalnya [&] atau [=]) agar sesuai dengan standar keselamatan tertentu, yang dapat membuat lambda membengkak karena setiap anggota harus ditangkap secara manual. Tidak semua organisasi melakukan hal ini, jadi saya hanya menyebutkan ini sebagai komentar, bukan sebagai jawaban. - person Human-Compiler; 20.08.2020

template<class T = int, class F>
void while_less(T n, F f, T start = 0){
    for(; start < n; ++start)
        f(start);
}

int main()
{
    int s = 0;
    
    while_less(10, [&](auto i){
        s += i;
    });
    
    assert(s == 45);
}

mungkin menyebutnya for_i

Tanpa biaya tambahan https://godbolt.org/z/e7asGj

person Hrisip    schedule 09.11.2020
comment
Tidak membuat argumen lambda auto const i sepertinya benar-benar menggagalkan intinya...! - person underscore_d; 06.02.2021
comment
@underscore_d intinya adalah membuat variabel counter tidak dapat dimodifikasi dalam loop, yaitu start dalam contoh saya. Mau i const atau tidak, itu terserah Anda, karena bukan itu intinya, meskipun bonusnya bagus. - person Hrisip; 06.02.2021