Когда я занимаюсь серьезным метапрограммированием, я превращаю все в типы.
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<template<class...>class> struct Z {};
template<class Z, class...Ts>
struct apply {};
template<template<class...>class z, class...ts>
struct apply< Z<z>, ts... >:
tag< z<ts...> >
{};
template<class Z, class...Ts>
using apply_t = type_t< apply<Z, Ts...> >;
теперь мы передаем template<?> foo
как Z<foo>
, и теперь это тип.
Аналогичные вещи можно сделать для констант, используя std::integral_constant<T, t>
(и проще использовать псевдонимы того же самого) или template<class T, T* p> struct pointer_constant {};
, превратив их в типы.
Как только все становится типом, ваше метапрограммирование становится более однородным. Шаблоны просто становятся видом типа, с которым apply_t
что-то делает.
В C++ нет способа иметь аргумент шаблона, который может быть типом, значением или шаблоном. Так что это лучшее, что вы можете получить.
шаблоны, не написанные для приведенного выше шаблона, должны быть обернуты, а их аргументы «подняты» до типов. В качестве примера:
template<class T, class t>
using number_constant = std::integral_constant< T, t{} >;
using number_constant_z = Z<number_constant>;
его аргументы "подняты" от значений к типам, а затем он был обернут Z
, чтобы превратиться в тип.
Bind теперь читает:
template<class z, class... Ts>
struct Bind {
template<class... More>
using type_base = apply_t< z, Ts..., More... >;
using type = Z<type_base>;
};
template<class Z, class...Ts>
using Bind_t = type_t<Bind<Z,Ts...>>; // strip ::type
using Bind_z = Z<Bind_t>; // quote into a Z<?>
а Bind_z
— это тип, обертывающий шаблон, который возвращает обернутый шаблон и принимает тип, обертывающий шаблон, в качестве первого аргумента.
Чтобы использовать его:
template<class...>struct types{using type=types;};
using types_z=Z<types>;
template<class...Ts>
using prefix =apply_t< Bind_z, types_z, Ts... >;
using prefix_z = Z<prefix>;
prefix_z
берет набор типов и генерирует фабрику types<?...>
, которая сначала будет содержать префикс Ts...
.
apply_t< apply_t< prefix_z, int, double, char >, std::string >
is
types< int, double, char, std::string >
живой пример.
Есть еще один забавный подход: делать метапрограммирование в функциях:
template<template<class...>class z, class...Ts>
constexpr auto apply_f( Z<z>, tag<Ts>... )
-> tag<z<Ts...>> { return {}; }
здесь типы представлены значениями типа tag<t>
, шаблоны — Z<z>
, а значения — std::integral_constant<?>
.
Эти двое:
template<class T>
constexpr tag<T> Tag = {};
template<template<class...>class z>
constexpr Z<z> Zag = {};
дать вам способы получить значения, которые представляют типы и шаблоны соответственно.
#define TYPEOF(...) type_t<decltype(__VA_ARGS__)>
— это макрос, который переходит от экземпляра tag
к типу типа в теге, а Tag<?>
перемещается от типа к экземпляру тега.
TYPEOF( apply_f( apply_f( Zag<prefix>, Tag<int>, Tag<double>, Tag<char> ), Tag<std::string> ) )
is
apply_t< apply_t< prefix_z, int, double, char >, std::string >
странно, но может быть интересно.
person
Yakk - Adam Nevraumont
schedule
17.08.2015
Rename<Map<Wrapper, TList>, Bind<AllTrue, TPredicate>>
) - person Vittorio Romeo   schedule 17.08.2015