std :: bind-like函数返回一个std :: function

时间:2015-03-20 19:33:51

标签: c++ templates c++11 bind

我需要一个行为与bind类似的std::bind函数,但会返回std::function的适当特化。

我认为std::function模板参数可以使用函数参数中的占位符编号来提取。尽管如此,这看起来并不重要。有没有可用的实现?


为什么我需要这个

我想实现一个waterfall函数,其语义类似于this JavaScript one

这就是我想象它在C ++中的样子:

std::function<void(const std::string &)> f = waterfall(
  []( const std::function<void(int)> &cb, const std::string & ) {
    ...;
    cb( 1 );
  },
  []( const std::function<void(double, double)> &cb, int ) {
    ...;
    cb(0.5, 10);
  },
  []( double, double ) {
  }
);

换句话说,waterfall将采用一堆函数,每个函数(但最后一个)将函数作为第一个参数。 waterfall将每个函数绑定到前一个函数(当然从最后一个函数开始),并返回单个函数。

基本上瀑布应该是这样的:

// recursion termination: `waterfall` called with a single functions
template< typename Arg >
auto waterfall( const Arg& first ) -> decltype( first ) {
  return first;
}
// recursion: `waterfall` called with mulitple functions
template< typename Arg, typename... Args >
... waterfall( const Arg& first, Args... args ) {
  return std::bind( first, waterfall(std::forward<Args>(args)...) );
}

但有三个未解决的问题:

  1. 在使用多个参数调用时确定waterfall的返回类型。它不能是decltype( std::bind(first, waterfall(...)) )(因为C ++不允许递归调用模板函数来推断其类型)。如果我知道函数的类型(即Arg),那么在没有第一个参数的情况下,它将是我正在寻找的返回类型。
  2. 我想我需要知道first的参数数量才能正确std::bind
  3. std::bind的返回类型并不像我想要的那样:嵌套std::bind调用将所有绑定函数合并到一起,而不是编写它们。
  4. 我能够通过编写std::bind包装器来绕过第三点,该包装器将绑定函数包装到T类型的对象中,使std::is_bind_expression<T>::value == false

    对于前两点,我需要找出waterfall参数的返回类型和参数类型。如果函数是std::function s,这将是微不足道的。如果它们是lambdas,经典函数或带有单operator()的仿函数也很简单:我只需要使用this function_traits之类的东西。但是,我确实喜欢将使用std::bind绑定的函数传递给waterfall,而无需手动将它们转换为std::function,因为这会让我代码方式更短,方式更清晰,大waterfall秒。

    任何想法,想法或建议?

1 个答案:

答案 0 :(得分:2)

这里的设计是在辅助类details::waterfall中完成大部分工作,并在waterfall_call中完成双功能瀑布式组合。

#include <iostream>
#include <utility>
#include <functional>
#include <string.h>

namespace details {

如果我在结构定义之外进行衰减,这会稍微有效,因为在某些情况下它会减少唯一类型。我不在乎:

  template<class F0, class F1>
  struct waterfall_call {
    typename std::decay<F0>::type f0;
    typename std::decay<F1>::type f1;

调用f0传递f1和args ...:

    template<class...Args>
    auto operator()(Args&&... args)const
    -> typename std::result_of<F0 const&(F1 const&,Args...)>::type
    {
      return f0( f1, std::forward<Args>(args)... );
    }
  };

算法的中心:

  struct waterfall {

对于2个参数,只需使用waterfall_call

    template<class F0, class F1>
    waterfall_call<F0, F1> operator()( F0&& f0, F1&& f1 )const
    {
      return { std::forward<F0>(f0), std::forward<F1>(f1) };
    }

对于&gt; 2个args,递归,然后执行2个arg解决方案:

    template<class F0, class...Fs,
      class I=typename std::result_of<waterfall const&(Fs...)>::type
    >
    auto operator()( F0&& f0, Fs&&... fs )const
    -> typename std::result_of< waterfall( F0, I ) >::type
    {
      auto child = (*this)( std::forward<Fs>(fs)... );
      return (*this)( std::forward<F0>(f0), std::move(child) );
    }

对于1个arg,只需转发到输入arg的衰减版本:

    template<class F0>
    auto operator()(F0&&f0)const
    ->typename std::decay<F0>::type
    {
      return std::forward<F0>(f0);
    }
  };
}

waterfall本身只是代表details::waterfall

template<class...Fs>
auto waterfall(Fs&&... fs )
-> typename std::result_of<details::waterfall(Fs...)>::type{
  return details::waterfall{}( std::forward<Fs>(fs)... );
}

live example

绕过递归模板返回类型演绎限制的另一种方法(除了上面的struct技巧)是从函数的命名空间中按模板类型获取参数,并将其传递给递归调用。这样就可以启用ADL查找,即使在推断自己的返回类型时也可以递归查找自己的函数。