Bagaimana saya bisa melakukan iterasi melalui tupel (menggunakan C ++ 11)? Saya mencoba yang berikut ini:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
tapi ini tidak berhasil:
Kesalahan 1: maaf, tidak diterapkan: tidak dapat memperluas 'Pendengar ...' menjadi daftar argumen dengan panjang tetap.
Kesalahan 2: Saya tidak dapat muncul dalam ekspresi konstan.
Jadi, bagaimana cara saya mengulang dengan benar elemen tupel?
Jawaban:
Boost.Fusion adalah kemungkinan:
Contoh yang belum teruji:
sumber
Saya memiliki jawaban berdasarkan Iterasi atas Tuple :
Ide yang biasa adalah menggunakan rekursi waktu kompilasi. Sebenarnya ide ini digunakan untuk membuat printf yang bertipe safe seperti yang tertera pada kertas tuple original.
Ini dapat dengan mudah digeneralisasikan menjadi
for_each
untuk tupel:Meskipun ini kemudian membutuhkan usaha untuk
FuncT
merepresentasikan sesuatu dengan kelebihan beban yang sesuai untuk setiap jenis yang mungkin berisi tupel. Ini berfungsi paling baik jika Anda tahu semua elemen tupel akan berbagi kelas dasar yang sama atau yang serupa.sumber
enable_if
dokumentasi .for_each
. Faktanya, saya melakukannya sendiri. :-) Saya rasa jawaban ini akan lebih berguna jika sudah digeneralisasikan.const std::tuple<Tp...>&
.. Jika Anda tidak bermaksud untuk memodifikasi tupel saat melakukan iterasi,const
versi tersebut sudah cukup.Di C ++ 17, Anda dapat menggunakan
std::apply
dengan ekspresi lipat :Contoh lengkap untuk mencetak tupel:
[Contoh Online di Coliru]
Solusi ini memecahkan masalah urutan evaluasi dalam jawaban M. Alaggan .
sumber
((std::cout << args << '\n'), ...);
? Lambda dipanggil sekali dengan tuple-elements yang dibuka sebagaiargs
, tapi ada apa dengan tanda kurung ganda?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
sini.Di C ++ 17 Anda dapat melakukan ini:
Ini sudah berfungsi di Clang ++ 3.9, menggunakan std :: eksperimental :: apply.
sumber
do_something()
- terjadi dalam urutan yang tidak ditentukan, karena paket parameter diperluas dalam panggilan fungsi()
, di mana argumen memiliki urutan yang tidak ditentukan? Itu mungkin sangat penting; Saya membayangkan kebanyakan orang akan mengharapkan pemesanan dijamin untuk terjadi dalam urutan yang sama dengan anggota, yaitu sebagai indeksstd::get<>()
. AFAIK, untuk mendapat jaminan pemesanan dalam kasus seperti ini, perluasan harus dilakukan di dalam{braces}
. Apakah aku salah? Jawaban ini menekankan pada pengurutan tersebut: stackoverflow.com/a/16387374/2757035Gunakan Boost.Hana dan lambda generik:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
sumber
using namespace boost::fusion
(terutama bersamausing namespace std
). Sekarang tidak ada cara untuk mengetahui apakah itufor_each
adalahstd::for_each
atauboost::fusion::for_each
C ++ memperkenalkan pernyataan ekspansi untuk tujuan ini. Mereka awalnya berada di jalur untuk C ++ 20 tetapi nyaris gagal karena kurangnya waktu untuk tinjauan kata-kata bahasa (lihat di sini dan sini ).
Sintaks yang saat ini disetujui (lihat tautan di atas) adalah:
sumber
Cara yang lebih sederhana, intuitif, dan ramah kompiler untuk melakukan ini di C ++ 17, menggunakan
if constexpr
:Ini adalah rekursi waktu kompilasi, mirip dengan yang disajikan oleh @emsr. Tetapi ini tidak menggunakan SFINAE jadi (menurut saya) ini lebih ramah kompiler.
sumber
Anda perlu menggunakan metaprogramming template, di sini ditunjukkan dengan Boost.Tuple:
Di C ++ 0x, Anda dapat menulis
print_tuple()
sebagai fungsi template variadic.sumber
Pertama-tama tentukan beberapa pembantu indeks:
Dengan fungsi Anda, Anda ingin menerapkan pada setiap elemen tupel:
kamu bisa menulis:
Atau jika
foo
kembalivoid
, gunakanCatatan: Pada C ++ 14
make_index_sequence
sudah ditentukan ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Jika Anda memang membutuhkan urutan evaluasi kiri-ke-kanan, pertimbangkan sesuatu seperti ini:
sumber
foo
kevoid
sebelum memanggiloperator,
untuk menghindari kemungkinan kelebihan beban operator patologis.Berikut cara C ++ 17 yang mudah untuk melakukan iterasi pada item tuple hanya dengan pustaka standar:
Contoh:
Keluaran:
Ini dapat diperpanjang untuk memutus loop secara kondisional jika callable mengembalikan nilai (tetapi masih berfungsi dengan callable yang tidak mengembalikan nilai bool yang dapat dialihkan, misalnya void):
Contoh:
Keluaran:
sumber
Jika Anda ingin menggunakan std :: tuple dan memiliki compiler C ++ yang mendukung template variadic, coba kode di bawah (diuji dengan g ++ 4.5). Ini harus menjadi jawaban atas pertanyaan Anda.
boost :: fusion adalah opsi lain, tetapi membutuhkan jenis tuple sendiri: boost :: fusion :: tuple. Mari lebih baik berpegang pada standar! Ini tesnya:
kekuatan template variadic!
sumber
Di MSVC STL ada fungsi _For_each_tuple_element (tidak didokumentasikan):
sumber
Yang lain telah menyebutkan beberapa perpustakaan pihak ketiga yang dirancang dengan baik yang dapat Anda gunakan. Namun, jika Anda menggunakan C ++ tanpa pustaka pihak ketiga tersebut, kode berikut dapat membantu.
Catatan: Kode dikompilasi dengan kompiler apa pun yang mendukung C ++ 11, dan menjaga konsistensi dengan desain pustaka standar:
Tupel tidak perlu
std::tuple
, dan sebagai gantinya dapat berupa apapun yang mendukungstd::get
danstd::tuple_size
; secara khusus,std::array
danstd::pair
dapat digunakan;Tuple mungkin merupakan tipe referensi atau kualifikasi cv;
Ini memiliki perilaku yang mirip dengan
std::for_each
, dan mengembalikan inputUnaryFunction
;Untuk pengguna C ++ 14 (atau versi laster),
typename std::enable_if<T>::type
dantypename std::decay<T>::type
dapat diganti dengan versi yang disederhanakan,std::enable_if_t<T>
danstd::decay_t<T>
;Untuk pengguna C ++ 17 (atau versi laster),
std::tuple_size<T>::value
dapat diganti dengan versi sederhananyastd::tuple_size_v<T>
,.Untuk pengguna C ++ 20 (atau versi laster),
SFINAE
fitur ini dapat diimplementasikan denganConcepts
.sumber
Menggunakan
constexpr
danif constexpr
(C ++ 17) ini cukup sederhana dan mudah:sumber
Saya mungkin ketinggalan kereta ini, tetapi ini akan ada di sini untuk referensi di masa mendatang.
Inilah konstruksi saya berdasarkan jawaban ini dan intinya :
Anda kemudian menggunakannya sebagai berikut:
Mungkin masih ada ruang untuk perbaikan.
Sesuai kode OP, itu akan menjadi ini:
sumber
Dari semua jawaban yang pernah saya lihat di sini, di sini dan di sini , saya menyukai cara terbaik @sigidagi untuk mengulang. Sayangnya, jawabannya sangat bertele-tele yang menurut saya mengaburkan kejelasan yang melekat.
Ini adalah versi saya dari solusinya yang lebih ringkas dan berfungsi dengan
std::tuple
,std::pair
danstd::array
.Demo: coliru
C ++ 14
std::make_index_sequence
dapat diimplementasikan untuk C ++ 11 .sumber
tuple meningkatkan menyediakan fungsi pembantu
get_head()
danget_tail()
sehingga fungsi pembantu Anda mungkin terlihat seperti ini:seperti dijelaskan di sini http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
dengan
std::tuple
itu harus serupa.Sebenarnya, sayangnya
std::tuple
tampaknya tidak menyediakan antarmuka seperti itu, jadi metode yang disarankan sebelumnya harus berfungsi, atau Anda perlu beralih keboost::tuple
yang memiliki manfaat lain (seperti operator io sudah disediakan). Meskipun ada sisi negatif dariboost::tuple
gcc - ia belum menerima template variadic, tapi itu mungkin sudah diperbaiki karena saya tidak memiliki versi boost terbaru yang diinstal pada mesin saya.sumber
Saya telah tersandung pada masalah yang sama untuk iterasi atas tupel objek fungsi, jadi inilah satu solusi lagi:
Keluaran:
sumber
Pilihan lain adalah mengimplementasikan iterator untuk tupel. Ini memiliki keuntungan bahwa Anda dapat menggunakan berbagai algoritme yang disediakan oleh pustaka standar dan loop berbasis rentang. Pendekatan elegan untuk ini dijelaskan di sini https://foonathan.net/2017/03/tuple-iterator/ . Ide dasarnya adalah mengubah tupel menjadi rentang dengan
begin()
danend()
metode untuk menyediakan iterator. Iterator itu sendiri mengembalikan astd::variant<...>
yang kemudian bisa dikunjungi menggunakanstd::visit
.Berikut beberapa contohnya:
Implementasi saya (yang sangat didasarkan pada penjelasan di tautan di atas):
Akses hanya baca juga didukung dengan meneruskan
const std::tuple<>&
keto_range()
.sumber
Memperluas jawaban @Stypox, kita dapat membuat solusi mereka lebih umum (C ++ 17 dan seterusnya). Dengan menambahkan argumen fungsi yang dapat dipanggil:
Kemudian, diperlukan strategi untuk mengunjungi setiap jenisnya.
Mari kita mulai dengan beberapa pembantu (dua yang pertama diambil dari cppreference):
variant_ref
digunakan untuk memungkinkan status tupel dimodifikasi.Pemakaian:
Hasil:
Untuk kelengkapannya, inilah my
Bar
&Foo
:sumber