【问题标题】:Partial Binding of Function Arguments函数参数的部分绑定
【发布时间】:2014-02-08 14:31:34
【问题描述】:

有没有办法部分地绑定可调用对象(例如函数)的第一个/最后一个n个参数而不显式指定其余参数?

std::bind() 似乎要求所有参数都被绑定,那些要留下的应该被绑定到std::placeholders::_1,_2,_3 等等。

是否可以为从第一个/最后一个参数开始的部分绑定编写 bind_first()/bind_last(),并自动将任何剩余未绑定参数的占位符按原始顺序插入到原始位置?

【问题讨论】:

  • 有一个std::bind1st,但它已被弃用。
  • 我担心这不可能实现
  • 这写起来其实很简单。您需要做的就是将绑定的参数存储在tuple 中,在operator() 中接受可变数量的参数,通过索引解包元组,然后附加实际参数。
  • Here's a rough sketch。对应的postbind 应该很容易从那里编写。
  • FWIW,在 C++14 中我们有通用的 lambda,这让整个事情变得更加容易。 Live example.

标签: c++ c++11 boost-bind currying stdbind


【解决方案1】:

Boost 和标准库bind 都不会自动填空。如果你有一个下雨的晚上,你可以自己写一个这样的小工具;这是一个仅用于普通函数的尾随参数的示例:

#include <tuple>
#include <type_traits>
#include <utility>

template <typename F, typename ...Args> struct trailing_binder;

template <typename R, typename ...Frgs, typename ...Args>
struct trailing_binder<R(Frgs...), Args...>
{
    template <typename ...Brgs>
    trailing_binder(R (*f)(Frgs...), Brgs &&... brgs)
    : the_function(f)
    , the_args(std::forward<Brgs>(brgs)...)
    { }

    template <unsigned int ...I> struct intlist {};

    template <typename ...Brgs>
    typename std::enable_if<sizeof...(Brgs) + sizeof...(Args) == sizeof...(Frgs), R>::type
    operator()(Brgs &&... brgs)
    {
        return unwrap(std::integral_constant<bool, 0 == sizeof...(Args)>(),
                      intlist<>(),
                      std::forward<Brgs>(brgs)...);
    }

private:
    template <unsigned int ...I, typename ...Brgs>
    R unwrap(std::false_type, intlist<I...>, Brgs &&... brgs)
    {
        return unwrap(std::integral_constant<bool, sizeof...(I) + 1 == sizeof...(Args)>(),
                      intlist<I..., sizeof...(I)>(),
                      std::forward<Brgs>(brgs)...);
    }

    template <unsigned int ...I, typename ...Brgs>
    R unwrap(std::true_type, intlist<I...>, Brgs &&... brgs)
    {
        return the_function(std::get<I>(the_args)..., std::forward<Brgs>(brgs)...);
    }

    R (*the_function)(Frgs...);
    std::tuple<Args...> the_args;
};

template <typename R, typename ...Args, typename ...Frgs>
trailing_binder<R(Frgs...), Args...> trailing_bind(R (*f)(Frgs...), Args &&... args)
{
    return trailing_binder<R(Frgs...), typename std::decay<Args>::type...>(f, std::forward<Args>(args)...);
}

用法:

int f(int a, int b, int c, int d) { return a + b + c + d; }

int main()
{
    auto b = trailing_bind(f, 1);
    return b(3, 8, 13);
}

【讨论】:

  • 我建议您查看我关于该问题的评论链接。 :)
  • @Xeo:您​​的意思是所有编译器错误的链接? :-) 请记住,目标类似于bind(f, 1),它返回一个带有 3 个参数的可调用对象。
  • 关于问题,而不是其他答案。 :P
  • 不使用任何构造函数,真的。它是一个聚合体。 :) (这实际上只是我的懒惰。那样没有适当的封装。)
【解决方案2】:

受这个问题的启发,我从头开始编写了自己的预绑定。虽然它最终看起来和其他所有人都非常相似,但我保证它是原创的 :) - 称之为趋同进化。

不过味道稍有不同。一方面,它转发给它的构造函数,但您可能更喜欢使用std::decay(它在某些方面更有意义,但我不喜欢到处写std::ref)。另外,我添加了对嵌套预绑定的支持,因此 prebind(foo, prebind(GetRandomNumber))()prebind(foo)(GetRandomNumber()) 相同。

#include <tuple>
#include <type_traits>
using namespace std;

struct pb_tag {}; //use inheritance to mark prebinder structs

//result_of_t will be defined by default in c++1y
template<typename T > using result_of_t = typename result_of<T>::type;
template<typename T> using is_prebinder = is_base_of<pb_tag, typename remove_reference<T>::type >;

//ugly sequence generators for something different
template<int N, int ...S> struct seq : seq<N-1, N, S...> {};
template<int ...S> struct seq<0, S...> {typedef seq type;};

//these three functions are only for nested prebind. they map
//T t -> T t and Prebind<f, T...> -> f(T...)
template<typename T>
auto dispatchee(T&& t, false_type) -> decltype(forward<T>(t)){
    return forward<T>(t);
}

template<typename T>
auto dispatchee(T&& t, true_type) -> decltype(t())
{
    return t();
}

template<typename T>
auto expand(T&& t) -> decltype(dispatchee(forward<T>(t), is_prebinder<T>()))
{
    return dispatchee(forward<T>(t), is_prebinder<T>());
}

template<typename T> using expand_type = decltype(expand(declval<T>()));

//the functor which holds the closure in a tuple
template<typename f, typename ...ltypes>
struct prebinder : public pb_tag
{
    tuple<f, ltypes...> closure;
    typedef typename seq<sizeof...(ltypes)>::type sequence;
    prebinder(f F, ltypes... largs) : closure(F, largs...) {}

    template<int ...S, typename ...rtypes>
    result_of_t<f(expand_type<ltypes>..., rtypes...)>
    apply(seq<0, S...>, rtypes&& ... rargs){
        return (get<0>(closure))(expand(get<S>(closure))... , forward<rtypes>(rargs)...);
    }

    template<typename ...rtypes>
    result_of_t<f(expand_type<ltypes>..., rtypes...)>
    operator() (rtypes&& ... rargs){
        return apply(sequence(), forward<rtypes>(rargs)...);
    }
};

template<typename f, typename ...ltypes>
prebinder<f, ltypes...> prebind(f&& F, ltypes&&... largs)
{
    return prebinder<f, ltypes...>(forward<f>(F), forward<ltypes>(largs)...);
}

也可以轻松更改为 postbind。

用法如下:

int g(int a){ return 1 + a; }

int h(){ return 1; }

int i(int a, int b, int c, int d){
    return 1 + a + b + c + d;
}

int main()
{
    //completely bound
    auto a = prebind(g, 1);
    cout << a() << endl;

    //nested bind by reference
    auto b = prebind(g, a);
    cout << b() << endl;
    get<1>(a.closure) = 2;
    cout << b() << endl;

    //bind to prebinder
    auto c = prebind(b);
    cout << c() << endl;

    //nested bind of temp to temp
    auto d = prebind(prebind(g), prebind(h));
    cout << d() << endl;

    //and the one you wanted orginally
    auto e = prebind(i, 1, 1, 1);
    cout << e(1) << endl;

    return 0;
}

【讨论】:

  • 你确定吗?我敢肯定,我无数次都这么想,但总是发现这不是真的。
  • 我修正了我的答案,给出了一个实际的预绑定,live example.
【解决方案3】:

现在的建议是避免 Bind 并使用 lambda 来包装对您的成员函数的调用。

https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-avoid-bind.html

【讨论】:

    猜你喜欢
    • 2014-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    相关资源
    最近更新 更多