2016-05-27 4 views
2

Liebe Internet Menschen, aufzurufenC++ eine Template-Parameter Packung mit mehreren Templat Funktionen

Ich bin derzeit eine Variante Klasse in C++ 14 und benötigen das Schreiben eine Template-Funktion für jedes Element in den Parametern nennen Pack. Nach einigem Suchen stieß ich auf this Seite, die das folgende Beispiel kennzeichnet.

template<typename... Ts> void func(Ts... args){ 
    const int size = sizeof...(args) + 2; 
    int res[size] = {1,args...,2}; 
    // since initializer lists guarantee sequencing, this can be used to 
    // call a function on each element of a pack, in order: 
    int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... }; 
} 

Ich habe dieses kleine Beispiel erstellt, das demonstriert, was ich zu erreichen versuche.

#include <iostream> 

template<typename Ta, typename Tb> struct TypeCmp 
{ 
    static constexpr bool Value = false; 
}; 

template<typename T> struct TypeCmp<T, T> 
{ 
    static constexpr bool Value = true; 
}; 

template<typename T, typename... Ts> struct TypeIdCounter; 

template<typename T> struct TypeIdCounter<T> 
{ 
    static constexpr size_t Value = 1; 
}; 

template<typename T, typename Tcur, typename... Ts> struct TypeIdCounter<T, Tcur, Ts...> 
{ 
    static constexpr size_t Value = sizeof(Tcur)+(TypeCmp<T, Tcur>::Value ? 0 : TypeIdCounter<T, Ts...>::Value); 
}; 

template<typename... Ts> struct TypeHolder 
{ 
    template<typename T> struct Info 
    { 
     static constexpr size_t Id = TypeIdCounter<T, Ts...>::Value; 
     static constexpr size_t Size = sizeof(T); 

     static void Print(size_t id) 
     { 
      if (Id == id) 
      { 
       std::cout << "Type::Id = " << Id << std::endl; 
       std::cout << "Type::Size = " << Size << std::endl; 
      } 
     } 
    }; 

    template<typename T> TypeHolder(const T& value) : id(Info<T>::Id) 
    { 
     /* copy value to container */ 
    } 

    void Print() const 
    { 
     int dummy[] = {(Info<Ts>::Print(id), 0)...}; 
     if (dummy[0]) 
     { 
      /* dummy test needed! */ 
     } 
    } 

    size_t id; 
}; 

struct Foo 
{ 
    std::string name; 
    int age; 
}; 

typedef TypeHolder<int, long long, bool, Foo> MyTypes; 

int main(int argc, char* args[]) 
{ 
    std::cout << "Id(int): " << MyTypes::Info<int>::Id << std::endl; 
    std::cout << "Id(bool): " << MyTypes::Info<bool>::Id << std::endl; 
    std::cout << "Id(Foo): " << MyTypes::Info<Foo>::Id << std::endl; 

    MyTypes types(true); 

    types.Print(); 

    return 0; 
} 

Das oben aufgelistete Programm generiert die folgende Ausgabe.

Id(int): 4 
Id(bool): 13 
Id(Foo): 53 
Type::Id = 13 
Type::Size = 1 

Wie immer kompilieren ich meinen Code mit den -Wall -Werror Fahnen. Also die if (dummy[0])... Bedingung wird benötigt, sonst bekomme ich einen unused variable 'dummy' Fehler.

Ich bin total ahnungslos, warum int dummy[] = {(Info<Ts>::Print(id), 0)...}; scheint zu arbeiten, da Info::Print ist eine void-Methode. Könnte mich jemand aufklären und erklären, warum das funktioniert? Und gibt es eine Möglichkeit, den int dummy[] = {(...)}; if (dummy[0]); Trick zu verhindern, ohne -Wall -Werror fallen zu lassen?

Ich habe versucht, nach einer Erklärung zu suchen, aber da ich keine Ahnung habe, wie diese Konstruktion überhaupt heißt, ist es nicht einfach, etwas zu finden.

+2

Sie sollten 'int dummy [Größe von ... (Ts)] = {(void (std :: cout << args), 0) ...} sagen;' wenn Sie diesen Trick verwenden, um unerwünschte ADL zu vermeiden und Operator Überlastung Nebenwirkungen –

+0

Ist die Zeile 'Int res [Größe] ...' versehentlich eingefügt? – feersum

+1

Schlüsselwörter: 'Komma Operator', 'Parameter Packs'. – feersum

Antwort

2
using dummy=int[]; 
(void)dummy{0, 
    ((void)(Info<Ts>::Print(id)), 0)... 
}; 

wird

int dummy[] = {(Info<Ts>::Print(id), 0)...}; 

ersetzen und alle Warnungen nicht generieren sollte. Es behandelt auch Listen mit 0 Längen.

Der , Operator wird void Typen nehmen. Es ist eingebaut und magisch.

Die Verwendung von (void) neben Print oben stellt sicher, dass, wenn der Rückgabetyp von Print nicht ist void, und die Art Überlastungen operator,(blah, 0) zurückkehren, wissen wir nicht ein unerwartetes Verhalten bekommen.

Ich selbst, auf Compiler, die es unterstützen, schreibe ich invoke_for_each.

template<class T>struct tag_type{using type=T;}; 
template<class T>constexpr tag_type<T> tag{}; 

template<class F, class...Ts> 
void invoke_for_each(F&& f, Ts&&...ts) { 
    using dummy=int[]; 
    (void)dummy{0, 
    ((void)(f(std::forward<Ts>(ts)), 0)... 
    }; 
}; 

und jetzt am Einsatzort:

invoke_for_each(
    [&](auto tag){ 
    using T=typename decltype(tag)::type; 
    Info<T>::Print(id); 
    }, 
    tag<Ts>... 
); 

, wo wir die magischen Array Sachen aus der Sicht der Person, den Code zu lesen bewegen.

+0

Danke das ist genau das, was ich suche. – Charlie

Verwandte Themen