2016-04-14 10 views
2

Dieser Code von Benutzer Faheem Mitha, basiert auf Benutzer Johannes Schaub - Litbs Antwort in diesem SO. Dieser Code macht genau das, was ich suche, nämlich die Umwandlung eines tuple in ein Parameterpaket, aber ich verstehe diesen Code nicht gut genug und dachte daher, dass ich eine neue Diskussion erstellen werde, die bei der Metaprogrammierung von Neulingen wie mir helfen könnte. Also, bitte verzeihen Sie die doppelte Buchung.Tuple zu Parameter pack

nun auf den Code bewegen

#include <tuple> 
#include <iostream> 
using std::cout; 
using std::endl; 

template<int ...> struct seq {}; 

template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { }; 

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; }; 

double foo(int x, float y, double z) 
{ 
    return x + y + z; 
} 

template <typename ...Args> 
struct save_it_for_later 
{ 
    std::tuple<Args...> params; 
    double(*func)(Args...); 

    double delayed_dispatch() 
    { 
     return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1 
    } 

    template<int ...S> 
    double callFunc(seq<S...>) 
    { 
     return func(std::get<S>(params) ...); 
    } 
}; 

int main(void) 
{ 
    std::tuple<int, float, double> t = std::make_tuple(1, (float)1.2, 5); 
    save_it_for_later<int, float, double> saved = { t, foo }; 
    cout << saved.delayed_dispatch() << endl; 
    return 0; 
} 

Ich bin 1 oben durch Artikel # völlig verwirrt:

  • Welchen Zweck hat typename auf dieser Linie dienen?
  • Ich verstehe, dass gens<sizeof...(Args)>::type() auf gens<3>::type() erweitert wird, aber das scheint weder template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { }; noch template<int ...S> struct gens<0, S...> zu entsprechen. Ich vermisse den Punkt offensichtlich und würde mich freuen, wenn jemand erklären könnte, was hier passiert.

Ich verstehe, dass callFunc in dieser Form aufgerufen wird callFunc(seq<0,1,2>) und die return-Anweisung dieser Methode selbst zu return func(std::get<0>(params), std::get<1>(params), std::get<2>(params) erweitert und das ist, was diese Regelung Arbeit mache, aber ich kann nicht trainieren, wie dieser seq<0,1,2> Typ erzeugt wird.

Hinweis: Mit std::index_sequence_for ist keine Option, mein Compiler unterstützt keine C++ 14 Funktionen.

PS: Kann diese Technik als Template-Metaprogrammierung klassifiziert werden?

+1

1) Um den Compiler zu informieren, dass 'gens :: type' ist ein Typ, und nicht etwa eine Member-Funktion. 2) Parameterpakete können leer sein. –

+1

'gens <3>' passt auch zu 'template struct gens', wobei' N == 3' und 'S' ein leeres Paket sind. –

+1

Grundsätzlich ist 'seq' [std :: index_sequence'] (http://en.cppreference.com/w/cpp/utility/integer_sequence) und' gens' ist 'std :: make_index_sequence' –

Antwort

7

Blick Lassen Sie uns an, was passiert hier:

template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { }; 

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; }; 

Die erste ist eine generische Vorlage, die zweite ist eine Spezialisierung, die gilt, wenn der erste Template-Parameter 0 ist

Nun nehmen Sie ein Stück Papier und Bleistift, und aufschreiben, wie

gens<3> 

durch die obige Vorlage definiert wird. Wenn Ihre Antwort war:

struct gens<3> : public gens<2, 2> 

dann hatten Sie Recht. So wird die erste Vorlage erweitert, wenn N "3" ist und ...S leer ist. gens<N - 1, N - 1, S...> wird daher gens<2, 2>. Jetzt

, lasst uns weitermachen und sehen, wie gens<2, 2> definiert wird:

struct gens<2, 2> : public gens<1, 1, 2> 

Hier in der Vorlage Expansion, N 2, und ...S "2" ist. Nun wollen wir den nächsten Schritt machen, und sehen, wie gens<1, 1, 2> definiert:

struct gens<1, 1, 2> : public gens<0, 0, 1, 2> 

Ok, jetzt wie funktioniert gens<0, 0, 1, 2> definiert wird?Es kann nun durch die Spezialisierung definiert werden:

template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; }; 

Also, was passiert mit struct gens<0, 0, 1, 2> hier? Nun, in der Spezialisierung "S ..." wird zu "0, 1, 2", so wird dies in einer Art und Weise des Sprechens:

struct gens<0, 0, 1, 2> { 

    typedef seq<0, 1, 2> type; 

} 

Nun bedenken Sie, dass alle diese öffentlich erben von jeder andere, „Elefanten-Stil“, so: sein eine typedef-Deklaration für

struct seq<0, 1, 2> 

gens<3>::type 

endet Und das verwendet wird, um den Code, der das Tupel in einen Parameter Pack zu konvertieren folgt mit einer anderen Vorlage:

double delayed_dispatch() 
{ 
    return callFunc(typename gens<sizeof...(Args)>::type()); // Item #1 
} 

...Args sind die Tupelparameter. Wenn es also drei Elemente in dem Tupel gibt, ist sizeof(...Args) 3, und wie oben erläutert, wird gens<sizeof...(Args)>::type() zu gens<3>::type(), a.k.a. seq<0, 1, 2>().

So, jetzt:

template<int ...S> 
double callFunc(seq<S...>) 
{ 
    return func(std::get<S>(params) ...); 
} 

Der S... Teil wird "0, 1, 2", so die

std::get<S>(params)... 

Wird ein Parameter Pack, die erweitert wird:

std::get<0>(params), std::get<1>(params), std::get<2>(params), 

Und so wird aus einem Tupel ein Parameter-Pack.

+0

Vielen Dank für die sehr klare Erklärung. – DigitalEye

Verwandte Themen