我试图编写一个Bind
元编程模板辅助元函数,它将模板参数绑定到某个东西。
我有一个简单模板元函数的工作实现:
template<typename T0, typename T1>
struct MakePair
{
using type = std::pair<T0, T1>;
};
template<template<typename...> class TF, typename... Ts>
struct Bind
{
template<typename... TArgs>
using type = TF<Ts..., TArgs...>;
};
using PairWithInt = typename Bind<MakePair, int>::type;
static_assert(std::is_same<PairWithInt<float>, MakePair<int, float>>{}, "");
但是如果MakePair
的模板参数是模板模板呢?还是简单的数值?
template<template<typename> class T0, template<typename> class T1>
struct MakePair0
{
using type = /*...*/;
};
template<template<typename...> class TF, template<typename> class... Ts>
struct Bind0 { /*...*/ }
// ...
template<int T0, int T1>
struct MakePair1
{
using type = /*...*/;
};
template<template<int...> class TF, int... Ts>
struct Bind1 { /*...*/ }
许多不必要的重复。如果模板参数在类型,模板模板和整数常量之间混合,则无法管理。
是否可以使用以下代码?
template<template<ANYTHING...> class TF, ANYTHING... Ts>
struct BindAnything
{
template<ANYTHING... TArgs>
using type = TF<Ts..., TArgs...>;
};
ANYTHING
会接受类型,模板模板,模板模板模板,整数值等...
答案 0 :(得分:9)
当我进行严肃的元编程时,我将所有转换为类型。
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
做事的类型 kind 。
C ++中没有办法拥有一个可以是类型,值或模板的模板参数。所以这是你能得到的最好的。
不是为上述模式编写的模板需要被包装起来,并且他们的参数被解除了#34;成为类型。举个例子:
template<class T, class t>
using number_constant = std::integral_constant< T, t{} >;
using number_constant_z = Z<number_constant>;
已经有了自己的论点&#34;解除了#34;从值到类型,然后它被Z
包裹起来,使自己变成一个类型。
现在绑定:
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 >
是
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> ) )
是
apply_t< apply_t< prefix_z, int, double, char >, std::string >
奇怪,但可能很有趣。
答案 1 :(得分:3)
我认为你在这里寻找quote
和map
。首先,你想要的东西给出了一个&#34;元功能类&#34;并且一系列参数为您提供了一种新类型:
template <typename MCls, typename... Args>
using map = typename MCls::template apply<Args...>;
正如此处的实现所暗示的,元函数类是具有名为apply
的成员别名模板的元函数类。
要将类模板转换为元函数类,我们引入quote
:
template <template <typename...> class C>
struct quote {
template <typename... Args>
using apply = C<Args...>;
};
以上内容足以完成以下内容:
using T = map<quote<std::tuple>, int, char, double>;
产生类型:
std::tuple<int, char, double>
在你的例子中,我们可以写:
using P = map<quote<MakePair>, int, char>::type; // std::pair<int, char>
但我更愿意直接将MakePair
作为元函数类:
struct MakePair2 {
template <typename T, typename U>
using apply = std::pair<T, U>;
};
using P = map<MakePair2, int, char>; // also std::pair<int, char>
避免了额外的::type
。
始终使用元函数(具有名为type
的成员typedef的类型,例如map
)和元函数类(a键入一个名为apply
的成员模板别名,例如quote
),并在整个元编程代码中仅使用那些概念。价值观和班级模板是二等公民。