這是一種在C++中進行currying的方法,在對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), {} }; }
上面的代碼編譯使用GCC 4.8的快照(禁止拷貝和粘貼錯誤),條件是在一個indices
類型和indices_for
效用。 This question並且其答案證實了這種事情的需要和實施,其中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
控制)。在這方面仍然有可能提出不同的政策(例如系統性衰減)。
請注意,參數(但不是函數)在最終調用中傳遞時具有相同的值類別,因爲它們傳遞給curried包裝器。因此,在
auto functor = curry([](foo f, int) {});
auto curried = functor(foo {});
auto r0 = curried(0);
auto r1 = curried(1);
這意味着一個計算r1
時被傳遞到底層函子移動-從foo
。
你說沒有提升,但爲什麼?如果你不想使用庫,你仍然可以複製它提供的功能。 – mydogisbox 2012-07-24 12:14:59
您的咖喱功能具有綁定功能。你可以替代地使用'自動FN =標準::綁定([](INT的x,int y)對{返回X * Y;},性病::佔位符:: _ 1,5);' – mkaes 2012-07-24 12:19:03
mydogisbox:由於在五年我一直在編碼,我對Boost嗤之以鼻。如果它足夠小,我可以重新實現一些功能,但我不期望Boost的功能很小。此外,其許多功能已成爲標準。然而,我總是願意被證明是錯誤的。 – SplinterOfChaos 2012-07-24 12:35:58