Rozważmy przypadek funkcji opartej na szablonie z różnymi argumentami szablonu:
template<typename Tret, typename... T> Tret func(const T&... t);
Teraz mam krotkę t
wartości. Jak wywołać, func()
używając wartości krotki jako argumentów? Czytałem o bind()
obiekcie funkcji, z call()
funkcją, a także o apply()
funkcji w różnych, nieaktualnych, dokumentach. Wydaje się, że implementacja GNU GCC 4.4 ma call()
funkcję w bind()
klasie, ale jest bardzo mało dokumentacji na ten temat.
Niektórzy sugerują ręcznie pisane rekurencyjne hacki, ale prawdziwą wartością argumentów z szablonów wariadycznych jest możliwość ich użycia w przypadkach takich jak powyżej.
Czy ktoś ma rozwiązanie na to, albo podpowiedź, gdzie o tym poczytać?
integer_sequence
, patrz en.cppreference.com/w/cpp/utility/integer_sequenceinteger_sequence S
, po prostu wywołujesz swoją funkcję jakofunc(std::get<S>(tuple)...)
i pozwalasz kompilatorowi zająć się resztą.Odpowiedzi:
Oto mój kod, jeśli ktoś jest zainteresowany
Zasadniczo w czasie kompilacji kompilator rekurencyjnie rozwinie wszystkie argumenty w różnych wywołaniach funkcji włączających <N> -> wywołania <N-1> -> wywołania ... -> wywołania <0>, który jest ostatnim, a kompilator przeprowadzi optymalizację różne wywołania funkcji pośrednich, aby zachować tylko ostatnią, która jest odpowiednikiem funkcji func (arg1, arg2, arg3, ...)
Podano dwie wersje, jedną dla funkcji wywoływanej na obiekcie, a drugą dla funkcji statycznej.
źródło
tr1
rzeczy można teraz usunąć za pomocą c ++ 11W C ++ 17 możesz to zrobić:
To już działa w Clang ++ 3.9, używając std :: experimental :: apply.
Odpowiadając na komentarz, który mówi, że to nie zadziała, jeśli
the_function
jest szablonem, można obejść ten problem :To obejście jest uproszczonym rozwiązaniem ogólnego problemu z przekazywaniem zestawów przeciążeń i szablonów funkcji, w przypadku których oczekiwana byłaby funkcja. Ogólne rozwiązanie (takie, które zapewnia doskonałe przekazywanie, constexpr-ness i noexcept-ness) jest przedstawione tutaj: https://blog.tartanllama.xyz/passing-overload-sets/ .
źródło
the_function
jest na szablonie.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
W C ++ istnieje wiele sposobów rozwijania / rozpakowywania krotki i stosowania tych elementów krotki do wariadycznej funkcji szablonu. Oto mała klasa pomocnicza, która tworzy tablicę indeksów. Jest często używany w metaprogramowaniu szablonów:
Teraz kod, który wykonuje tę pracę, nie jest taki duży:
Test jest pokazany poniżej:
Nie jestem wielkim znawcą innych języków, ale myślę, że jeśli te języki nie mają takiej funkcjonalności w swoim menu, to nie sposób tego zrobić. Przynajmniej z C ++ możesz, i myślę, że nie jest to tak bardzo skomplikowane ...
źródło
template<class ... T> void three(T...) {}
coś podobnego i spróbuję użyć Apply na tym, to się nie kompiluje.Uważam, że jest to najbardziej eleganckie rozwiązanie (i jest optymalnie przekazane):
Przykładowe użycie:
Niestety GCC (przynajmniej 4.6) nie kompiluje tego z "przepraszam, nie zaimplementowano: zniekształcone przeciążenie" (co po prostu oznacza, że kompilator nie implementuje jeszcze w pełni specyfikacji C ++ 11), a ponieważ używa szablonów wariadycznych, nie będzie działa w MSVC, więc jest mniej lub bardziej bezużyteczna. Jednak gdy pojawi się kompilator obsługujący specyfikację, będzie to najlepsze podejście IMHO. (Uwaga: nie jest trudno to zmodyfikować, aby można było obejść braki w GCC lub zaimplementować go z Boost Preprocessor, ale psuje elegancję, więc to jest wersja, którą publikuję).GCC 4.7 obsługuje teraz ten kod dobrze.
Edycja: Dodano przekierowanie wokół rzeczywistego wywołania funkcji, aby wspierać formularz odniesienia rvalue * w przypadku, gdy używasz clang (lub jeśli ktoś inny rzeczywiście zacznie go dodawać).
Edycja: Dodano brakujące przejście do przodu wokół obiektu funkcji w treści funkcji niebędącej składnikiem. Podziękowania dla pheedbaq za wskazanie, że go brakowało.
Edycja: A oto wersja C ++ 14 tylko dlatego, że jest o wiele ładniejsza (w rzeczywistości jeszcze się nie kompiluje):
Oto wersja dla funkcji składowych (niezbyt testowana!):
źródło
apply
dlaczego w funkcji niebędącej składowąf
nie jest opakowanastd::forward
wywołaniem, tak jak w typie zwracanym? Czy to nie jest potrzebne?foo('x', true)
skompilowałem dokładnie do tego samego kodu asemblera, coapply(foo, ::std::make_tuple('x', true))
z każdym poziomem optymalizacji poza -O0.integer_sequence
otrzymujesz nawet prawie poprawną implementacjęapply()
w jego przykładzie. zobacz moją odpowiedź poniżej.Jest to adaptowane z wersji roboczej C ++ 14 przy użyciu index_sequence. Mógłbym zaproponować zastosowanie w przyszłej normie (TS).
źródło
Wiadomości nie wyglądają dobrze.
Po przeczytaniu właśnie opublikowanej wersji roboczej standardu nie widzę wbudowanego rozwiązania tego problemu, co wydaje się dziwne.
Najlepszym miejscem do spytania o takie rzeczy (jeśli jeszcze tego nie zrobiłeś) jest comp.lang.c ++. Moderated, ponieważ niektórzy ludzie zajmują się tam regularnie redagowaniem standardowego postu.
Jeśli sprawdzisz ten wątek , ktoś ma to samo pytanie (może to ty, w takim przypadku cała odpowiedź będzie dla ciebie trochę frustrująca!) I sugerowanych jest kilka brzydkich implementacji.
Zastanawiałem się tylko, czy prościej byłoby sprawić, by funkcja akceptowała a
tuple
, ponieważ konwersja w ten sposób jest łatwiejsza. Ale to implikuje, że wszystkie funkcje powinny akceptować krotki jako argumenty, dla maksymalnej elastyczności, a to po prostu demonstruje dziwność braku wbudowanego rozwinięcia krotki do pakietu argumentów funkcji.Aktualizacja: powyższy link nie działa - spróbuj wkleić to:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661
źródło
Wszystkie te implementacje są dobre. Ale ze względu na użycie wskaźnika do funkcji składowej kompilator często nie może wbudować wywołania funkcji docelowej (przynajmniej gcc 4.8 nie może, bez względu na wszystko Dlaczego gcc nie może wbudować wskaźników funkcji, które można określić? )
Ale sytuacja się zmienia, jeśli wyślesz wskaźnik do funkcji składowej jako argumenty szablonu, a nie jako parametry funkcji:
I zastosowanie:
Dowód na inlinable http://goo.gl/5UqVnC
Przy niewielkich zmianach możemy „przeciążać”
apply_tuple
:Dodatkowo jest to jedyne rozwiązanie, które działa z funkcjami szablonowymi.
źródło
1) jeśli masz gotową strukturę parameter_pack jako argument funkcji, możesz po prostu użyć std :: tie w następujący sposób:
2) jeśli nie masz gotowego argumentu parampack, będziesz musiał rozwinąć krotkę w ten sposób
źródło
Co powiesz na to:
run_tuple
Szablon funkcja przyjmuje daną krotki i przekazać jego elementy indywidualnie do danej funkcji. Wykonuje swoją pracę, rekurencyjnie wywołując szablony funkcji pomocniczychexplode_tuple
. Ważne jest, abyrun_tuple
przekazać rozmiar krotki doexplode_tuple
; ta liczba działa jako licznik liczby elementów do wyodrębnienia.Jeśli krotka jest pusta,
run_tuple
wywołuje pierwszą wersjęexplode_tuple
z funkcją zdalną jako jedynym innym argumentem. Funkcja zdalna jest wywoływana bez argumentów i gotowe. Jeśli krotka nie jest pusta, wyższa liczba jest przekazywana do drugiej wersjiexplode_tuple
, wraz z funkcją remote. Rekurencyjne wywołanieexplode_tuple
jest tworzony z tymi samymi argumentami, z wyjątkiem tego, że numer licznika jest zmniejszany o jeden i (odwołanie do) ostatniego elementu krotki jest dołączany jako argument po funkcji zdalnej. W wywołaniu rekurencyjnym albo licznik nie jest zerem, a inne wywołanie jest wykonywane z licznikiem zmniejszonym ponownie, a następny element bez odniesienia jest wstawiany na listę argumentów po funkcji zdalnej, ale przed innymi wstawionymi argumentami, lub licznik osiąga zero i wywoływana jest funkcja zdalna ze wszystkimi argumentami zgromadzonymi po niej.Nie jestem pewien, czy mam poprawną składnię wymuszania określonej wersji szablonu funkcji. Myślę, że można użyć wskaźnika do funkcji jako obiektu funkcji; kompilator automatycznie to naprawi.
źródło
Oceniam MSVS 2013RC i w niektórych przypadkach nie udało mi się skompilować niektórych z poprzednich rozwiązań proponowanych tutaj. Na przykład MSVS nie skompiluje zwrotów „auto”, jeśli będzie zbyt wiele parametrów funkcji, z powodu limitu nasycenia przestrzeni nazw (wysłałem tę informację do firmy Microsoft w celu poprawienia). W innych przypadkach potrzebujemy dostępu do zwrotu funkcji, chociaż można to również zrobić za pomocą lamdy: poniższe dwa przykłady dają ten sam wynik ..
I jeszcze raz dziękuję tym, którzy przede mną opublikowali odpowiedzi, bez tego nie doszedłbym do tego ... więc oto:
źródło
const
?Rozszerzając rozwiązanie @ David, możesz napisać szablon rekurencyjny, który
integer_sequence
semantykiint N
do liczenia iteracji rekurencyjnychNa przykład:
Alternatywnie, jeśli twój funktor nie jest zdefiniowany w czasie kompilacji (np.
constexpr
Instancja niebędąca funktorem lub wyrażenie lambda), możesz użyć go jako parametru funkcji zamiast parametru szablonu klasy i faktycznie całkowicie usunąć klasę zawierającą:W przypadku wywołań funkcji wskaźnika do elementu członkowskiego możesz dostosować dowolny z powyższych fragmentów kodu podobnie jak w odpowiedzi @ David.
Wyjaśnienie
W odniesieniu do drugiego fragmentu kodu istnieją dwie funkcje szablonu: pierwsza pobiera funktor
func
, krotkęt
z typamiT...
i pakiet parametrówargs
z typamiArgs_tmp...
. Gdy jest wywoływana, rekurencyjnie dodaje obiekty odt
do pakietu parametrów po jednym naraz, od początku (0
) do końca, i wywołuje funkcję ponownie z nowym zwiększonym pakietem parametrów.Podpis drugiej funkcji jest prawie identyczny z pierwszym, z wyjątkiem tego, że używa typu
T...
dla pakietu parametrówargs
. Tak więc, gdyargs
pierwsza funkcja zostanie całkowicie wypełniona wartościami zt
, jej typ będzieT...
(w pseudo-kodzie,typeid(T...) == typeid(Args_tmp...)
), a zatem kompilator zamiast tego wywoła drugą przeciążoną funkcję, która z kolei wywołafunc(args...)
.Kod w przykładzie statycznego funktora działa identycznie, z funktorem zamiast tego używanym jako argument szablonu klasy.
źródło
Dlaczego po prostu nie zawinąć argumentów wariadycznych w klasę krotek, a następnie użyć rekursji czasu kompilacji (patrz link ) w celu pobrania indeksu, który Cię interesuje. Uważam, że rozpakowywanie szablonów wariadycznych do kontenera lub kolekcji może nie być typem bezpiecznym dla typów heterogenicznych
źródło
Args...
->tuple
, aletuple
->Args...
.To proste rozwiązanie działa dla mnie:
źródło