2017-08-28 1 views
2

Im folgenden C++ 14 Code möchte ich einen Container für PossibleTypes mit variadischen Vorlagen erstellen. Als nächstes möchte ich einen weiteren Container mit einem Member-Tupel der zuvor angegebenen Typen erstellen.Variablenarten wiederverwenden

Wie würde man dabei vorgehen? Ich hätte gerne, dass Foo ein Tupel von < int, double> enthält, genauso wie die Template-Typen von Themen.

Vielen Dank im Voraus.

#include <iostream> 
#include <tuple> 
#include <type_traits> 

template <typename... T> 
struct PossibleTypes {}; 

PossibleTypes<int,double> topics; 

template <typename... T> 
struct Foo{ 
    std::tuple<T...> member; 
}; 

int main(){ 
    Foo<??(topics)??> x; 
    return 0; 
} 

Antwort

10

Es gibt mehr als eine Möglichkeit, dies zu tun. Sie müssen sie nicht alle verstehen, sondern nur einen mit den gewünschten Funktionen finden.

Hier sind drei sehr unterschiedliche Möglichkeiten, dies zu tun.

Transkribieren von einer Instanz zu einer Vorlage

template<class Src, template<class...>class Dest> 
struct transcribe; 
template<class Src, template<class...>class Dest> 
using transcribe_t=typename transcribe<Src,Dest>::type; 

template<template<class...>class Src, class...Ts, template<class...>class Dest> 
struct transcribe<Src<Ts...>, Dest>{ 
    using type=Dest<Ts...>; 
}; 

dann:

transcribe_t<decltype(topics), Foo> x; 

intrusiv von Foo Modifizieren

Dies kann auch intrusiv durch Foo Modifizierung getan werden, um ein Bündel zu nehmen statt eine Packung.

template <class Bundle> 
struct Foo; 

template <typename... T> 
struct Foo<PossibleTypes<T...>>{ 
    std::tuple<T...> member; 
}; 

oder

template <template<class...>class Z, typename... T> 
struct Foo<Z<T...>>{ 
    std::tuple<T...> member; 
}; 

dann:

Foo<decltype(topics)> x; 

, die viel praktischer sein kann, wenn Sie gerade nicht in einem Satz von ... Argumente zu übergeben.

Wertorientiertes metaprogramming

Wir können auch diese mit wertorientierten Ansatz metaprogramming:

template<class T> 
struct tag_t {constexpr tag_t(){} using type=T;}; 
template<class T> 
constexpr tag_t<T> tag{}; 
template<template<class...>class Z> 
struct ztemplate_t { 
    constexpr ztemplate_t() {} 
    template<class...Ts> using apply=Z<Ts...>; 
    template<class...Ts> 
    constexpr tag_t<Z<Ts...>> operator()(tag_t<Ts>...)const{ return {}; } 
}; 
template<template<class...>class Z> 
constexpr ztemplate_t<Z> ztemplate{}; 

template<class Tag> 
using type=typename Tag::type; 

template <class... T> 
struct PossibleTypes { 
    template<template<class...>class Z> 
    constexpr auto operator()(ztemplate_t<Z> z) const { 
    return z(tag<T>...); 
    } 
}; 

geben Sie uns:

int main(){ 
    type<decltype(topics(ztemplate<Foo>)) > x; 
    return 0; 
} 

, die ziemlich glatt ist. Live example.

tag hebt einen Typ in einen Wert auf. ztemplate hebt eine Vorlage in einen Wert auf.

ztemplate<some_template>(tag<T0>, tag<T1>) gibt eine tag des Ergebnisses der some_template-T0, T1 wie tag_t<some_template<T0, T1>> Anwendung.

Um von einem Tag zu einem Typ zurückzukehren, machen wir einen type<decltype(some_tag_expression)>.

Ich habe Ihre PossibleTypes geändert, um auch eine operator()(ztemplate) zu haben, die die Vorlage auf die in PossibleTypes gespeicherten Typen anwendet.

Diese Art von Wahnsinn funktioniert besser, da Sie mehr und mehr typbasierte Manipulation vornehmen, da die wertebasierte Programmierung in C++ viel aussagekräftiger und einfacher zu verwenden ist als die Vorlagensyntax.

+0

@mihai Keiner der oben genannten erfordert das Objekt instanziieren? In jedem Fall erstelle ich den Typ. Dann, weil Ihr Code das getan hat, erstelle ich eine Instanz des Typs (um zu beweisen, dass es funktioniert hat, denke ich). Entfernen Sie dann 'x' und Sie haben einen Typ Ausdruck und keine Instanz. Verwenden Sie typedef oder verwenden Sie, um ihm einen Namen oder was auch immer zu geben. – Yakk

+0

Arf leid. Problem in einem anderen Teil des Codes. Funktioniert super! –