3

我正在尝试编写一个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将接受类型、模板模板、模板模板模板、整数值等...

4

2 回答 2

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<?> fooas 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>;

已经将它的参数从值“提升”到类型,然后用 a 包装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<?>

andBind_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 {}; }

在这里,类型由 type tag<t>、模板 aZ<z>和值 as的值表示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__)>

是一个宏,它从 a 的实例移动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 >

奇怪,但可能很有趣。

于 2015-08-17T18:10:36.373 回答
4

我想你正在寻找quotemap在这里。首先,你想要一个给定“元函数类”的东西,并且一系列参数给你一个新类型:

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.

始终使用元函数(具有名为的成员 typedef 的类型type,例如map)和元函数类(具有名为的成员模板别名的类型apply,例如quote)的概念,并在整个元编程代码中仅使用这些概念。值和类模板是二等公民。

于 2015-08-17T18:28:57.480 回答