在C++元编程中为模板进行Currying

hae*_*lix 11 c++ templates bind metaprogramming currying

这更像是一个概念性问题.我正在尝试找到将两个arg模板(参数类型)转换为一个arg模板的最简单方法.即,绑定其中一种类型.

这将是bindboost/std中的元编程等价物.我的例子包括一个可能的用例,即std::is_same作为模板参数传递给一个模板,该模板采用一个arg模板模板参数(std::is_same是一个双arg模板),即TypeList::FindIf.在TypeList没有完全在这里实现,也不是FindIf,但你的想法.它采用"一元谓词"并返回该谓词为真的void类型,如果不是这样的类型.

我有两个工作变体,但第一个不是单行,第二个使用相当冗长的BindFirst装置,这对非类型模板参数不起作用.有没有一种简单的方法来编写这样的单行程?我相信我正在寻找的程序被称为currying.

#include <iostream>

template<template<typename, typename> class Function, typename FirstArg>
struct BindFirst
{
    template<typename SecondArg>
    using Result = Function<FirstArg, SecondArg>;
};

//template<typename Type> using IsInt = BindFirst<_EqualTypes, int>::Result<Type>;
template<typename Type> using IsInt = std::is_same<int, Type>;


struct TypeList
{
    template<template<typename> class Predicate>
    struct FindIf
    {
        // this needs to be implemented, return void for now
        typedef void Result;
    };
};

int main()
{

  static_assert(IsInt<int>::value, "");
  static_assert(!IsInt<float>::value, "");


  // variant #1: using the predefined parameterized type alias as predicate
  typedef TypeList::FindIf<IsInt>::Result Result1;

  // variant #2: one-liner, using BindFirst and std::is_same directly
  typedef TypeList::FindIf< BindFirst<std::is_same, int>::Result>::Result Result2;

  // variant #3: one-liner, using currying?
  //typedef TypeList::FindIf<std::is_same<int, _>>::Result Result2;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

单击此处查看在线编译器GodBolt中的代码.

Bar*_*rry 3

我认为执行此操作的典型方法是将所有内容保留在类型世界中。不要采用模板模板——它们很混乱。让我们编写一个名为的元函数ApplyAnInt,它将采用“元函数类”并应用于int它:

template <typename Func>
struct ApplyAnInt {
    using type = typename Func::template apply<int>;
};
Run Code Online (Sandbox Code Playgroud)

一个简单的元函数类可能只是检查给定类型是否为int

struct IsInt {
    template <typename T>
    using apply = std::is_same<T, int>;
};

static_assert(ApplyAnInt<IsInt>::type::value, "");
Run Code Online (Sandbox Code Playgroud)

现在的目标是支持:

static_assert(ApplyAnInt<std::is_same<_, int>>::type::value, "");
Run Code Online (Sandbox Code Playgroud)

我们能做到这一点。我们将调用包含_“lambda 表达式”的类型,并编写一个名为 的元函数lambda,它将转发一个不是 lambda 表达式的元函数类,或者生成一个新的元函数(如果它是):

template <typename T, typename = void>
struct lambda {
    using type = T;
};

template <typename T>
struct lambda<T, std::enable_if_t<is_lambda_expr<T>::value>>
{
    struct type {
        template <typename U>
        using apply = typename apply_lambda<T, U>::type;
    };
};

template <typename T>
using lambda_t = typename lambda<T>::type;
Run Code Online (Sandbox Code Playgroud)

所以我们更新我们原来的元函数:

template <typename Func>
struct ApplyAnInt
{
    using type = typename lambda_t<Func>::template apply<int>;
};
Run Code Online (Sandbox Code Playgroud)

现在剩下两件事:我们需要is_lambda_exprapply_lambda。这些实际上并没有那么糟糕。对于前者,我们将查看它是否是类模板的实例化,其中类型之一是_

template <typename T>
struct is_lambda_expr : std::false_type { };

template <template <typename...> class C, typename... Ts>
struct is_lambda_expr<C<Ts...>> : contains_type<_, Ts...> { };
Run Code Online (Sandbox Code Playgroud)

对于apply_lambda,我们只需将 替换_为给定类型:

template <typename T, typename U>
struct apply_lambda;

template <template <typename...> class C, typename... Ts, typename U>
struct apply_lambda<C<Ts...>, U> {
    using type = typename C<std::conditional_t<std::is_same<Ts, _>::value, U, Ts>...>::type;
};
Run Code Online (Sandbox Code Playgroud)

这就是您实际上所需要的。我会将其扩展为支持,arg_<N>作为读者的练习。