用C ++元编程编写模板 [英] Currying for templates in C++ metaprogramming
问题描述
这更多是一个概念性的问题.我正在尝试找到将两个参数模板(参数为类型)转换为一个参数模板的最简单方法.即绑定一种类型.
This is more of a conceptual question. I'm trying to find the easiest way of converting a two-arg template (the arguments being types) into a one-arg template. I.e., binding one of the types.
这与boost/std中bind
的元编程等效.我的示例包括一个可能的用例,即将std::is_same
作为模板参数传递给采用单参数模板模板参数(std::is_same
是两个参数的模板)的模板,即传递给TypeList::FindIf
. TypeList
此处未完全实现,FindIf
也不完全实现,但是您明白了.它接受一个一元谓词",并返回该谓词为true的类型,如果不是,则返回void
.
This would be the meta-programming equivalent of bind
in boost/std. My example includes a possible use-case, which is, passing std::is_same
as template argument to a template that takes a one-arg template template argument (std::is_same
being a two-arg template), i.e. to TypeList::FindIf
. The TypeList
is not fully implemented here, neither is FindIf
, but you get the idea. It takes a "unary predicate" and returns the type for which that predicate is true, or void
if not such type.
我有2个工作变体,但第一个不是单行,第二个使用相当冗长的BindFirst
构造,不适用于非类型模板参数.有没有一种简单的方法可以编写这样的单行代码?我相信我要查找的过程称为currying
.
I have 2 working variants but the first is not a one-liner and the 2nd uses a rather verbose BindFirst
contraption, that would not work for non-type template arguments. Is there a simple way to write such a one-liner? I believe the procedure I'm looking for is called 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;
}
点击此处以获取在线编译器GodBolt中的代码.
Click here for code in online compiler GodBolt.
推荐答案
我认为这样做的典型方法是将所有内容都保留在类型世界中.不要使用模板模板-它们太乱了.让我们编写一个名为ApplyAnInt
的元函数,它将带有一个元函数类"并将其应用int
:
I think the typical way of doing this is keep everything in the world of types. Don't take template templates - they're messy. Let's write a metafunction named ApplyAnInt
that will take a "metafunction class" and apply int
to it:
template <typename Func>
struct ApplyAnInt {
using type = typename Func::template apply<int>;
};
简单的元函数类可能只是在检查给定类型是否为int
:
Where a simple metafunction class might be just checking if the given type is an 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表达式的元函数类,或者如果它是新的元函数,则该新的元函数类是: /p>
We can do that. We're going to call types that contain _
"lambda expressions", and write a metafunction called lambda
which will either forward a metafunction class that isn't a lambda expression, or produce a new metafunction if it is:
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;
因此,我们更新了原始的元功能:
So we update our original metafunction:
template <typename Func>
struct ApplyAnInt
{
using type = typename lambda_t<Func>::template apply<int>;
};
现在剩下两件事了:我们需要is_lambda_expr
和apply_lambda
.实际上,这些还算不错.对于前者,我们将查看它是否是类模板的实例化,其中类型之一是_
:
Now that leaves two things: we need is_lambda_expr
and apply_lambda
. Those actually aren't so bad at all. For the former, we'll see if it's an instantiation of a class template in which one of the types is _
:
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
,我们只是将_
替换为给定的类型:
And for apply_lambda
, we just will substitute the _
with the given type:
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>
作为练习,供读者阅读.
And that's all you need actually. I'll leave extending this out to support arg_<N>
as an exercise to the reader.
这篇关于用C ++元编程编写模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!