GeirGrusom Skrevet 9. desember 2015 Del Skrevet 9. desember 2015 Jeg ønsker å gjøre følgende: template<typename T> T Bar(int index) { return {}; } template<typename ... T> int Foo(const std::function<void(T...)> &proc) { int i = 0; proc(Bar<T>(i++)...); } Dette fungerer ikke helt (i++ i denne sammenhengen er udefinert oppførsel) og fører til at i Visual C++ så utføres argumentene i omvendt rekkefølge av GCC (i praksis teller den ned istedet for opp). Er det en bedre måte å uttrykke dette på? Jeg så en del eksempler med template i template, men så ikke hvordan det kunne løse dette. Lenke til kommentar
Lycantrophe Skrevet 9. desember 2015 Del Skrevet 9. desember 2015 Du kan generere en sekvens (med templates) av 0..N ved å rekursere over en template-int. Skal prøve å få opp et eksempel. Lenke til kommentar
Lycantrophe Skrevet 9. desember 2015 Del Skrevet 9. desember 2015 (endret) Hoi, da har vi i det minste et fungerende utgangspunkt. #include <iostream> #include <tuple> #include <functional> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply<N-1>::apply( ::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... )) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward<F>(f)(::std::forward<A>(a)...)) { return ::std::forward<F>(f)(::std::forward<A>(a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t))) { return Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); } template< typename T > T bar( int index ) { std::cout << "bar(" << index << ")" << std::endl; return { index }; } template< int N, typename T > std::tuple< T > xbar() { return std::make_tuple( bar< T >( N ) ); } template< int N, typename T, typename U, typename... Ts > std::tuple< T, U, Ts... > xbar() { return std::tuple_cat( std::make_tuple( bar< T >( N ) ), xbar< N + 1, U, Ts... >() ); } template< typename... T > int foo( const std::function< void(T...) >& proc ) { apply( proc, xbar< 0, T... >() ); return 0; } void f( int x ) { std::cout << "f(x): f(" << x << ")" << std::endl; } void g( int x, int y) { std::cout << "g(x,y): g(" << x << "," << y << ")" << std::endl; } int main() { const auto f1 = std::function< void(int) >( f ); const auto g1 = std::function< void(int, int) >( g ); foo(f1); foo(g1); } apply er tatt herfra: http://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments Til litt beskrivelse: xbar() er funksjonen som løser problemet. apply tar en tuple med parametere og gir til funksjonen f, slik at apply( f, make_tuple( 1, 2, 3, 4 ) ) blir det samme som f( 1, 2, 3, 4 ). Den er egentlig ikke en del av løsningenen, men jeg tok den med for å gjøre proc()-biten enkel. Det som egentlig skjer er at vi bruker templaterekursjon til å genere en sekvens 0..N og lager en tuple med tuple_cat, der hvert element i tuplen er bar(int) applied med riktig indeks. Det er trivielt å få den til å gå fra N..0 (start med sizeof...(T)-1 og bruk N - 1). På denne måten bevarer vi typeinformasjon samtidig som vi gir den riktig int. Output: bar(0) f(x): f(0) bar(1) bar(0) g(x,y): g(0,1) Det fine med dette er at det er rekursjon og 0-arity-functions, så evalueringsrekkefølge er veldefinert, i motsetning til om du skulle passet bar(0), bar(1)... til proc, som da ville vært uspesifisert. Dette bør ikke bety noe ettersom funksjonen din selvfølgelig er uten side effects, men uansett en kjekk garanti å ha. Endret 9. desember 2015 av Lycantrophe 4 Lenke til kommentar
Lycantrophe Skrevet 9. desember 2015 Del Skrevet 9. desember 2015 Forøvrig, det er også mulig å gjøre ved å bygge en serie rekurisve kall til en templatisert baz, der hvert rekrusive steg regner ut neste bar(i). i kan sendes som både template og som eksplisitt argument, og når rekursjonen terminerer kaller du proc der. Mistenker forøvrig at det da kan gå rimelig hardt utover ytelse, men det må definitivt måles for å kunne sies noe om. Lenke til kommentar
GeirGrusom Skrevet 10. desember 2015 Forfatter Del Skrevet 10. desember 2015 Det overrasker meg litt at dette er lov: template< int N, typename T > std::tuple< T > xbar() { return std::make_tuple( bar< T >( N ) ); } template< int N, typename T, typename U, typename... Ts > std::tuple< T, U, Ts... > xbar() { return std::tuple_cat( std::make_tuple( bar< T >( N ) ), xbar< N + 1, U, Ts... >() ); } Jeg trodde ikke templates kunne overloades? Lenke til kommentar
Lycantrophe Skrevet 10. desember 2015 Del Skrevet 10. desember 2015 (endret) Det er ikke en overload, det er en -spesialisering-. Kompilatoren plukker alltid den mest spesifikke den kan. Men jeg har brukt et triks. Legg merke til at det ikke-terminerende steget (N, T, U, Ts...) bruker TO parametere før variadic, og sender den ene av de sammen med ekspansjonen til rekursjonen. Det er fordi template< typename T = int > template< typename T = int, typename... Ts = {} > begge matcher når det bare er ett element igjen, og du er i basetilfellet. Noen ganger så holder det faktisk å bruke #2, men det er avhengig av hva som skjer med expansionen. I dette tilfellet kan vi ikke gjøre ingenting med en tom parameter pack, så vi trenger å hjelpe kompilatoren med å avgjøre hva som er basetilfellet og ikke. Det er i grunn ganske naturlig når en tenker litt over det, men jeg satt -lenge- med det første gangen jeg kom over det. edit: hm, kom til å tenke på at den faktisk ikke vil finne noenting dersom T... = {}, altså tom parameterliste, fra ytterste. Dersom det aldri kan skje at den listen er tom så er det ingen sak, men dersom det gir mening må du reformulere littegranne. Da vil det antagelig også holde å bruke Ts... fremfor T, U, Ts... edit2: hah! nevermind. Dette er langt bedre: template< int N > std::tuple<> xbar() { return {}; } template< int N, typename T, typename... Ts > std::tuple< T, Ts... > xbar() { return std::tuple_cat( std::make_tuple( bar< T >( N ) ), xbar< N + 1, Ts... >() ); } Mindre søl, og håndterer også tom liste. referanse: http://en.cppreference.com/w/cpp/utility/tuple Template parameters Types... - the types of the elements that the tuple stores. Empty list is supported. Endret 10. desember 2015 av Lycantrophe Lenke til kommentar
Anbefalte innlegg
Opprett en konto eller logg inn for å kommentere
Du må være et medlem for å kunne skrive en kommentar
Opprett konto
Det er enkelt å melde seg inn for å starte en ny konto!
Start en kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå