Jak mogę iterować po krotce (używając C ++ 11)? Wypróbowałem następujące:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
ale to nie działa:
Błąd 1: przepraszam, nie zaimplementowano: nie można rozwinąć „Listener ...” do listy argumentów o stałej długości.
Błąd 2: nie mogę pojawić się w stałym wyrażeniu.
Jak więc poprawnie wykonać iterację po elementach krotki?
Odpowiedzi:
Boost.Fusion to możliwość:
Nieprzetestowany przykład:
źródło
Mam odpowiedź opartą na iteracji po krotce :
Typowym pomysłem jest użycie rekursji w czasie kompilacji. W rzeczywistości ten pomysł jest używany do tworzenia printf, który jest bezpieczny dla typu, jak zauważono w oryginalnych dokumentach krotek.
Można to łatwo uogólnić na a
for_each
for krotki:Chociaż wymaga to pewnego wysiłku, aby
FuncT
przedstawić coś z odpowiednimi przeciążeniami dla każdego typu, który może zawierać krotka. Działa to najlepiej, jeśli wiesz, że wszystkie elementy krotki będą miały wspólną klasę bazową lub coś podobnego.źródło
enable_if
dokumentację .for_each
. W rzeczywistości zrobiłem to sam. :-) Myślę, że ta odpowiedź byłaby bardziej przydatna, gdyby była już uogólniona.const std::tuple<Tp...>&
... Jeśli nie zamierzasz modyfikować krotek podczas iteracji, teconst
wersje będą wystarczające.W C ++ 17, można korzystać
std::apply
z opuszczanym wypowiedzi :Kompletny przykład drukowania krotki:
[Przykład online w Coliru]
To rozwiązanie rozwiązuje kwestię kolejności ocen w odpowiedzi M. Alaggana .
źródło
((std::cout << args << '\n'), ...);
? Lambda jest wywoływana raz z rozpakowanymi elementami krotki jakoargs
, ale o co chodzi z podwójnymi nawiasami?((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))
.W C ++ 17 możesz to zrobić:
To już działa w Clang ++ 3.9, używając std :: experimental :: apply.
źródło
do_something()
- występujących w nieokreślonej kolejności, ponieważ pakiet parametrów jest rozszerzany w wywołaniu funkcji()
, w której argumenty mają nieokreśloną kolejność? To może być bardzo znaczące; Wyobrażam sobie, że większość ludzi oczekiwałaby, że porządkowanie będzie gwarantowane w tej samej kolejności co członkowie, tj. Jak indeksystd::get<>()
. AFAIK, aby w takich przypadkach uzyskać gwarancję zamówienia, rozszerzenie musi zostać wykonane w ciągu{braces}
. Czy się mylę? Ta odpowiedź kładzie nacisk na taką kolejność: stackoverflow.com/a/16387374/2757035Użyj Boost.Hana i generycznych lambd:
http://coliru.stacked-crooked.com/a/27b3691f55caf271
źródło
using namespace boost::fusion
(szczególnie razem zusing namespace std
). Teraz nie ma sposobu, aby wiedzieć, czy tofor_each
jeststd::for_each
alboboost::fusion::for_each
W tym celu C ++ wprowadza instrukcje rozszerzające . Początkowo byli na dobrej drodze do C ++ 20, ale ledwo przegapili cięcia z powodu braku czasu na przegląd tekstów językowych (zobacz tutaj i tutaj ).
Obecnie uzgodniona składnia (patrz linki powyżej) to:
źródło
Bardziej prosty, intuicyjny i przyjazny dla kompilatora sposób na zrobienie tego w C ++ 17, używając
if constexpr
:Jest to rekurencja w czasie kompilacji, podobna do tej przedstawionej przez @emsr. Ale to nie używa SFINAE, więc (myślę), że jest bardziej przyjazny dla kompilatora.
źródło
Musisz użyć metaprogramowania szablonów, pokazanego tutaj z Boost.
W C ++ 0x możesz
print_tuple()
zamiast tego pisać jako wariadyczną funkcję szablonu.źródło
Najpierw zdefiniuj kilka pomocników indeksujących:
Swoją funkcją chciałbyś zastosować do każdego elementu krotki:
Możesz pisać:
Lub w przypadku
foo
zwrotówvoid
użyjUwaga: W C ++ 14
make_index_sequence
jest już zdefiniowane ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).Jeśli potrzebujesz kolejności oceny od lewej do prawej, rozważ coś takiego:
źródło
foo
tovoid
przed wywołaniem,operator,
aby uniknąć możliwego przeciążenia operatora patologicznego.Oto prosty sposób na iterację w języku C ++ 17 po elementach krotek przy użyciu tylko standardowej biblioteki:
Przykład:
Wynik:
Można to rozszerzyć, aby warunkowo przerwać pętlę w przypadku, gdy funkcja wywoływana zwraca wartość (ale nadal działa z obiektami wywoływanymi, które nie zwracają wartości przypisywalnej typu bool, np. Void):
Przykład:
Wynik:
źródło
Jeśli chcesz używać std :: tuple i masz kompilator C ++, który obsługuje szablony wariadyczne, wypróbuj poniższy kod (przetestowany z g ++ 4.5). To powinna być odpowiedź na Twoje pytanie.
boost :: fusion to kolejna opcja, ale wymaga ona własnego typu krotki: boost :: fusion :: tuple. Lepiej trzymajmy się standardu! Oto test:
moc wariadycznych szablonów!
źródło
W MSVC STL jest funkcja _For_each_tuple_element (nieudokumentowana):
źródło
Inni wspominali o dobrze zaprojektowanych bibliotekach innych firm, do których możesz się zwrócić. Jeśli jednak używasz C ++ bez tych bibliotek innych firm, może pomóc poniższy kod.
Uwaga: Kod kompiluje się z dowolnym kompilatorem obsługującym C ++ 11 i zachowuje spójność z projektem biblioteki standardowej:
Krotka nie musi być
std::tuple
, a zamiast tego może być wszystkim, co obsługujestd::get
istd::tuple_size
; w szczególnościstd::array
istd::pair
mogą być używane;Krotka może być typu referencyjnego lub kwalifikowana jako CV;
Zachowuje się podobnie jak
std::for_each
i zwraca dane wejścioweUnaryFunction
;Dla użytkowników C ++ 14 (lub starszych wersji)
typename std::enable_if<T>::type
itypename std::decay<T>::type
można je zastąpić ich uproszczoną wersją,std::enable_if_t<T>
astd::decay_t<T>
;Dla C ++ 17 (lub laster wersja) użytkowników,
std::tuple_size<T>::value
można zastąpić jego uproszczonej wersjistd::tuple_size_v<T>
.W przypadku użytkowników C ++ 20 (lub starszych wersji)
SFINAE
funkcję można zaimplementować za pomocąConcepts
.źródło
Używanie
constexpr
andif constexpr
(C ++ 17) jest dość proste i proste:źródło
Mogłem przegapić ten pociąg, ale będzie tutaj do wykorzystania w przyszłości.
Oto moja konstrukcja oparta na tej odpowiedzi i na tym istocie :
Następnie używasz go w następujący sposób:
Mogłoby być miejsce na ulepszenia.
Zgodnie z kodem OP wyglądałoby to tak:
źródło
Ze wszystkich odpowiedzi, które widziałem tutaj, tutaj i tutaj , najbardziej podobał mi się sposób iteracji @sigidagi . Niestety, jego odpowiedź jest bardzo rozwlekła, co moim zdaniem przesłania naturalną jasność.
To jest moja wersja jego rozwiązanie, które jest bardziej zwięzły i działa
std::tuple
,std::pair
istd::array
.Demo: coliru
C ++ 14
std::make_index_sequence
można zaimplementować dla C ++ 11 .źródło
krotka Boost zapewnia funkcje pomocnicze
get_head()
iget_tail()
tak twoje funkcje pomocnicze mogą wyglądać następująco:jak opisano tutaj http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html
z
std::tuple
nim powinno być podobnie.Właściwie niestety
std::tuple
nie wydaje się zapewniać takiego interfejsu, więc sugerowane wcześniej metody powinny działać lub musiałbyś przełączyć się na ten,boost::tuple
który ma inne zalety (np. Operatorzy io już dostarczeni). Chociażboost::tuple
gcc ma wadę - nie akceptuje jeszcze szablonów wariadycznych, ale może to już zostać naprawione, ponieważ nie mam zainstalowanej najnowszej wersji boost na moim komputerze.źródło
Natknąłem się na ten sam problem podczas iteracji po krotce obiektów funkcji, więc oto jeszcze jedno rozwiązanie:
Wynik:
źródło
Inną opcją byłoby zaimplementowanie iteratorów dla krotek. Ma to tę zaletę, że można używać różnych algorytmów udostępnianych przez bibliotekę standardową i pętli opartych na zakresie. Eleganckie podejście do tego wyjaśniono tutaj https://foonathan.net/2017/03/tuple-iterator/ . Podstawową ideą jest przekształcenie krotek w zakres
begin()
iend()
metody zapewniające iteratory. Iterator sam zwraca a,std::variant<...>
który można następnie odwiedzić za pomocąstd::visit
.Oto kilka przykładów:
Moja realizacja (która jest w dużej mierze oparta na wyjaśnieniach w linku powyżej):
Dostęp w trybie tylko do odczytu jest również obsługiwany przez przekazanie
const std::tuple<>&
doto_range()
.źródło
Rozwijając odpowiedź @Stypox, możemy uczynić ich rozwiązanie bardziej ogólnym (od C ++ 17). Dodając wywoływalny argument funkcji:
Następnie potrzebujemy strategii odwiedzenia każdego typu.
Zacznijmy od kilku pomocników (pierwsze dwa pobrane z cppreference):
variant_ref
jest używany, aby umożliwić modyfikację stanu krotek.Stosowanie:
Wynik:
Aby uzyskać kompletność, oto moje
Bar
&Foo
:źródło