Der zweite ist aus vielen Gründen schlecht.
Erstens, es anzurufen ist ein Durcheinander. Vorlagen innerhalb von Vorlagen erfordern die Verwendung des Schlüsselworts template
.
Zweitens erfordert es, dass Ihre Typenliste jede Operation enthält, die Sie auf Typenlisten in seinem Körper ausführen möchten. Es ist, als würde man jede Operation auf einer string
als eine Methode für die Zeichenfolge definieren: Wenn Sie freie Funktionen zulassen, können neue Operationen erstellt werden, und Sie können sogar Überschreibungen implementieren.
Schließlich betrachten die ::type
versteckt:
Beginnen Sie mit diesen Primitiven:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class...Ts>struct types : tag<types<Ts...>>{};
Transformation oder fmap
, wie sieht dann: kann
template<template<class...>class Z, class Types>
struct fmap;
template<template<class...>class Z, class...Ts>
struct fmap<Z, types<Ts...>>:types<Z<Ts...>>{};
template<template<class...>class Z, class Types>
using fmap_t = type_t<fmap<Z,Types>>;
und Sie entweder type_t<fmap<Z,types<int,double>>>
verwenden, oder fmap_t<Z,types<int,double>>
, um die Typen des zugeordneten Typs abzurufen.
Ein weiterer Ansatz ist constexpr
Funktionen zu verwenden, die verschiedene Dinge enthalten:
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class Tag>using type_t=typename Tag::type;
template<template<class...>class Z>
struct z {template<class...Ts>using apply=Z<Ts...>; constexpr z(){};};
template<class...Ts>
struct one_type {};
template<class T0>
struct one_type<T0> { using type=T0; };
template<class...Ts>
using one_type_t=typename one_type<Ts...>::type;
template<template<class>class Z>
struct z_one_base {
template<class...Ts>
using helper = Z<one_type_t<Ts...>>;
using type = z<helper>;
};
template<template<class>class Z>
using z_one = type_t<z_one_base<Z>>;
jetzt fmap
ist einfach:
// take a template metafunction and a list of types
// and apply the metafunction to each type, returning the list
template<template<class...>class Z, class...Ts>
constexpr auto fmap(z<Z>, types<Ts...>)
-> types<Z<Ts>...> { return {}; }
und andere Funktionen folgen:
// a template metafunction and a list of types
// and apply the template metafunction to all of the types
template<template<class...>class Z, class...Ts>
constexpr auto apply(z<Z>, types<Ts...>)
-> tag<Z<Ts...>> { return {}; }
// take any number of tags
// and make a type list from them
template<class...Tags>
constexpr auto make_list(Tags...)
-> types<type_t<Tags>...> { return {}; }
// concat of nothing is an empty list
constexpr types<> concat() { return {}; }
// concat of a list alone is a list alone:
template<class...T1s>
constexpr auto concat(types<T1s...>)
->types<T1s...>{ return {}; }
// concat of 2 or more lists is the concat of the first two,
// concatted with the rest
template<class...T1s, class...T2s, class...Types>
constexpr auto concat(types<T1s...>,types<T2s...>,Types...)
->decltype(concat(types<T1s...,T2s...>{},Types{}...))
{ return {}; }
// take a tagged list or a tagged type, and return a list
template<class T>
constexpr auto fbox(tag<T>)->types<T> { return {}; }
template<class...Ts>
constexpr auto fbox(tag<types<Ts...>>)->types<Ts...> { return {}; }
// create z_ versions of functions above:
#define CAT2(A,B) A##B
#define CAT(A,B) CAT2(A,B)
// lift functions to metafunctions with z_ prefix:
#define Z_F(F) \
template<class...Ts> \
using CAT(meta_, F) = decltype(F(Ts{}...)); \
using CAT(CAT(z_, F),_t) = z<CAT(meta_, F)>; \
static constexpr CAT(CAT(z_, F),_t) CAT(z_, F){}
Z_F(concat);
//Z_F(apply);
//Z_F(fmap);
Z_F(fbox);
static constexpr z_one<tag> z_tag{};
// joins a list of lists or types into a list of types
template<class...Ts>
constexpr auto join1(types<Ts...>)
->type_t<decltype(apply(z_concat, fmap(z_fbox, types<tag<Ts>...>{})))>
{ return {}; }
template<class Types>
constexpr auto join(Types types)
->type_t<decltype(apply(z_concat, fmap(z_fbox, fmap(z_tag, types))))>
{ return {}; }
template<class Z, class...Ts>
constexpr auto fbind(Z z, Ts...ts)
->decltype(join(fmap(z, ts...)))
{ return {}; }
und Arbeit mit Pseudo-Typen (tag
s) statt mit Typen direkt auf der obersten Ebene.Wenn Sie zu Typen mit type_t
zurückkehren möchten, wenn Sie möchten.
Ich denke, das ist ein boost::hana
wie Ansatz, aber ich habe nur angefangen zu betrachten boost::hana
. Der Vorteil hier ist, dass wir die Typenbündel von den Operationen entkoppeln, wir erhalten Zugang zu vollständiger C++ - Überladung (anstelle des Vorlagenmusterabgleichs, der fragiler sein kann), und wir können den Inhalt der Typenbündel direkt ableiten, ohne dass Mach die using
und leere-primäre Spezialisierung Tricks.
Alles, was verbraucht wird, ist ein verpackter Typ von tag<?>
oder types<?>
oder z<?>
, also nichts ist "echt".
Prüfregeln:
template<class T> using to_double = double;
template<class T> using to_doubles = types<double>;
int main() {
types< int, int, int > three_ints;
auto three_double = fmap(z_one<to_double>{}, three_ints);
three_double = types<double, double, double >{};
auto three_double2 = join(fmap(z_one<to_doubles>{}, three_ints));
three_double = three_double2;
auto three_double3 = fbind(z_one<to_doubles>{}, three_ints);
three_double3 = three_double2;
}
Live example.
Es ist 'typename L :: Template-Transformation' vs. 'transform '. –
Ah, hmm, hatte die Hässlichkeit der Telefonvorwahl nicht berücksichtigt. –