c++ - C++ 元编程中的模板柯里化(Currying)

标签 c++ templates metaprogramming bind currying

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

这将是 boost/std 中 bind 的元编程等价物。我的示例包括一个可能的用例,即将 std::is_same 作为模板参数传递给采用单参数模板模板参数的模板 (std::is_same 是一个双参数模板),即 TypeList::FindIfTypeList 未在此处完全实现,FindIf 也未完全实现,但您明白了。它采用“一元谓词”并返回该谓词为真的类型,如果不是这种类型,则返回 void

我有 2 个工作变体,但第一个不是单行的,第二个使用相当冗长的 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;
}

点击here用于在线编译器 GodBolt 中的代码。

最佳答案

我认为这样做的典型方法是保持类型世界中的所有内容。不要使用模板模板——它们很乱。让我们编写一个名为 ApplyAnInt 的元函数这将采用“元函数类”并应用 int对它:

template <typename Func>
struct ApplyAnInt {
    using type = typename Func::template apply<int>;
};

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

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

static_assert(ApplyAnInt<IsInt>::type::value, "");

现在的目标是支持:

static_assert(ApplyAnInt<std::is_same<_, int>>::type::value, "");

我们可以做到。我们将调用包含 _ 的类型“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;

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

template <typename Func>
struct ApplyAnInt
{
    using type = typename lambda_t<Func>::template apply<int>;
};

现在还剩下两件事:我们需要 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...> { };

对于 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;
};

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

关于c++ - C++ 元编程中的模板柯里化(Currying),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27124560/

相关文章:

c++ - C++重载运算符<<和运算符>>

c++ - 是否可以检查两个类是否具有相同的成员

c++ - 为什么模板参数失去常量性?

ruby-on-rails - Rails 关注点 : class method with block

Groovy:如何在 setProperty() 中设置属性并避免无限递归?

c++ - 共享库错误为 "invalid conversion"从 void * 到 double(*) (int*)?

c++ - QWidget Initialization 在函数调用中的不同行为

c++ - 偏函数模板特化

ruby-on-rails - rails 中的验证在内部使用元编程?它实际上是如何工作的?

c++ - std::list 线程 push_back、front、pop_front