これはC++でカリー化する方法であり、OPの最近の編集後に関連する場合と関連しない場合があります。
過負荷のため、ファンクターを検査してそのアリティを検出することは非常に問題があります。ただし、可能なことは、ファンクターf
と引数が与えられた場合、が有効な式であるa
かどうかを確認できることです。f(a)
そうでない場合は、を格納a
して次の引数を指定すると、が有効な式であるb
かどうかを確認できます。f(a, b)
ウィットに:
#include <utility>
#include <tuple>
/* Two SFINAE utilities */
template<typename>
struct void_ { using type = void; };
template<typename T>
using Void = typename void_<T>::type;
// std::result_of doesn't play well with SFINAE so we deliberately avoid it
// and roll our own
// For the sake of simplicity this result_of does not compute the same type
// as std::result_of (e.g. pointer to members)
template<typename Sig, typename Sfinae = void>
struct result_of {};
template<typename Functor, typename... Args>
struct result_of<
Functor(Args...)
, Void<decltype( std::declval<Functor>()(std::declval<Args>()...) )>
> {
using type = decltype( std::declval<Functor>()(std::declval<Args>()...) );
};
template<typename Functor, typename... Args>
using ResultOf = typename result_of<Sig>::type;
template<typename Functor, typename... Args>
class curry_type {
using tuple_type = std::tuple<Args...>;
public:
curry_type(Functor functor, tuple_type args)
: functor(std::forward<Functor>(functor))
, args(std::move(args))
{}
// Same policy as the wrappers from std::bind & others:
// the functor inherits the cv-qualifiers from the wrapper
// you might want to improve on that and inherit ref-qualifiers, too
template<typename Arg>
ResultOf<Functor&(Args..., Arg)>
operator()(Arg&& arg)
{
return invoke(functor, std::tuple_cat(std::move(args), std::forward_as_tuple(std::forward<Arg>(arg))));
}
// Implementation omitted for brevity -- same as above in any case
template<typename Arg>
ResultOf<Functor const&(Args..., Arg)>
operator()(Arg&& arg) const;
// Additional cv-qualified overloads omitted for brevity
// Fallback: keep calm and curry on
// the last ellipsis (...) means that this is a C-style vararg function
// this is a trick to make this overload (and others like it) least
// preferred when it comes to overload resolution
// the Rest pack is here to make for better diagnostics if a user erroenously
// attempts e.g. curry(f)(2, 3) instead of perhaps curry(f)(2)(3)
// note that it is possible to provide the same functionality without this hack
// (which I have no idea is actually permitted, all things considered)
// but requires further facilities (e.g. an is_callable trait)
template<typename Arg, typename... Rest>
curry_type<Functor, Args..., Arg>
operator()(Arg&& arg, Rest const&..., ...)
{
static_assert( sizeof...(Rest) == 0
, "Wrong usage: only pass up to one argument to a curried functor" );
return { std::forward<Functor>(functor), std::tuple_cat(std::move(args), std::forward_as_tuple(std::forward<Arg>(arg))) };
}
// Again, additional overloads omitted
// This is actually not part of the currying functionality
// but is here so that curry(f)() is equivalent of f() iff
// f has a nullary overload
template<typename F = Functor>
ResultOf<F&(Args...)>
operator()()
{
// This check if for sanity -- if I got it right no user can trigger it
// It *is* possible to emit a nice warning if a user attempts
// e.g. curry(f)(4)() but requires further overloads and SFINAE --
// left as an exercise to the reader
static_assert( sizeof...(Args) == 0, "How did you do that?" );
return invoke(functor, std::move(args));
}
// Additional cv-qualified overloads for the nullary case omitted for brevity
private:
Functor functor;
mutable tuple_type args;
template<typename F, typename Tuple, int... Indices>
ResultOf<F(typename std::tuple_element<Indices, Tuple>::type...)>
static invoke(F&& f, Tuple&& tuple, indices<Indices...>)
{
using std::get;
return std::forward<F>(f)(get<Indices>(std::forward<Tuple>(tuple))...);
}
template<typename F, typename Tuple>
static auto invoke(F&& f, Tuple&& tuple)
-> decltype( invoke(std::declval<F>(), std::declval<Tuple>(), indices_for<Tuple>()) )
{
return invoke(std::forward<F>(f), std::forward<Tuple>(tuple), indices_for<Tuple>());
}
};
template<typename Functor>
curry_type<Functor> curry(Functor&& functor)
{ return { std::forward<Functor>(functor), {} }; }
indices
上記のコードは、タイプとindices_for
ユーティリティがある場合、GCC 4.8のスナップショット(コピーアンドペーストエラーを除く)を使用してコンパイルされます。この質問とその回答は、そのようなものの必要性と実装を示しています。ここで、(より便利な)seq
の役割を果たし、indices
実装gens
するために使用できますindices_for
。
上記の値のカテゴリと(可能性のある)一時的なものの寿命に関しては、細心の注意が払われています。curry
(および実装の詳細である付随するタイプ)は、非常に安全に使用できるようにしながら、可能な限り軽量になるように設計されています。特に、次のような使用法:
foo a;
bar b;
auto f = [](foo a, bar b, baz c, int) { return quux(a, b, c); };
auto curried = curry(f);
auto pass = curried(a);
auto some = pass(b);
auto parameters = some(baz {});
auto result = parameters(0);
コピーしないf
、a
またはb
; また、一時的なものへの参照がぶら下がることもありません。auto
これは、で置き換えられた場合でも当てはまります(正気であるとauto&&
仮定しますが、それはの制御を超えています)。その点でさまざまなポリシーを考え出すことはまだ可能です(たとえば、体系的に衰退する)。quux
curry
パラメータ(ファンクタではない)は、カレーラッパーに渡されるときと同じ値カテゴリで最終呼び出しに渡されることに注意してください。したがって、
auto functor = curry([](foo f, int) {});
auto curried = functor(foo {});
auto r0 = curried(0);
auto r1 = curried(1);
これは、をfoo
計算するときに、moved-fromが基になるファンクターに渡されることを意味しr1
ます。