在C ++中将成员函数绑定到对象实例的函数

时间:2014-12-07 21:00:33

标签: c++ c++11 variadic-templates stdbind

最近,我经常将成员函数绑定到对象的实例。我没有使用std :: bind,std :: mem_fn和std :: ref的组合,而是希望将所有这些组合成一个函数来自动处理它。

例如,请考虑以下代码:

#include <functional>
#include <iostream>
#include <string>

// This is the function I'd like to have working:
template <class T, typename RETURN_TYPE, typename... Arguments>
std::function<RETURN_TYPE(Arguments...)> obj_bind(RETURN_TYPE (T::*in_fun)(), T & obj, Arguments... params)
{
  return std::bind( std::mem_fn(in_fun), std::ref(obj), params...);
}

int main()
{
  // Standard use of push_back member function (our test case):
  std::string test_obj = "Test 1: ";
  test_obj.push_back('A');
  std::cout << test_obj << std::endl;

  // This WORKS:
  auto test_fun = std::bind( std::mem_fn(&std::string::push_back), std::ref(test_obj), 'B');

  // But I'd like to use this instead:
  // auto test_fun = obj_bind( &std::string::push_back, test_obj, 'C');

  test_obj = "Test 2: ";
  test_fun();
  std::cout << test_obj << std::endl;
}

我的obj_bind函数实际上对没有参数的成员函数工作正常,所以我很确定我的问题在于我是如何处理这些问题的,但是在几次尝试修复它之后,我认为我&# 39; d在这里寻求建议。

1 个答案:

答案 0 :(得分:2)

在深入修理活页夹之前,请注意以下几点:

  1. 您并不需要std::mem_fn()用于std::bind()的第一个参数,因为std::bind()已经知道如何处理成员函数指针。
  2. 获取成员函数地址的类型未定义。也就是说,你不能真正在std::string::push_back只是一个参数的成员函数指针。
  3. 您可以使用std::ref(testobj)而不是&testobj
  4. 如果需要有参数,你不能推断出这样的成员函数:你需要指定参数:

    template <class T, typename RETURN_TYPE, typename...Args, typename... Arguments>
    std::function<RETURN_TYPE(Arguments...)>
    obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params) {
        ...
    }
    

    这可以解决您的即时错误。下一个问题是你以某种方式将绑定参数与被调用函数的类型相关联。当然,这不是它的工作原理。在你的情况下,结果参数实际上不带任何参数,即你有这样的声明:

    template <class T, typename RETURN_TYPE, typename... Args, typename... Arguments>
    std::function<RETURN_TYPE()>
    obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params) {
        ...
    }
    

    如果您实际使用占位符和函数,则返回的std::function<RC(...)>实际上会有一些参数。弄清楚这些论点有点不重要。当将参数类型限制为指向函数的指针或指向成员函数的指针时,应该可以返回适当的函数对象。

    只是为了它的乐趣,这里有一个似乎与占位符有关的实现(虽然没有经过彻底的测试):

    #include <functional>
    #include <iostream>
    #include <string>
    
    using namespace std::placeholders;
    
    struct foo
    {
        void f(int i, double d, char c) {
            std::cout << "f(int=" << i << ", double=" << d << ", char=" << c << ")\n";
        }
    };
    
    template <typename...> struct type_list;
    
    template <typename, typename, typename, typename> struct objbind_result_type;
    template <typename RC, typename... RA>
    struct objbind_result_type<RC, type_list<RA...>, type_list<>, type_list<> > {
        typedef std::function<RC(RA...)> type;
    };
    
    template <typename RC,
              typename... RA,
              typename A0, typename... A,
              typename B0, typename... B>
    struct objbind_result_type<RC, type_list<RA...>,
                               type_list<A0, A...>,
                               type_list<B0, B...> >;
    
    template <bool, typename, typename, typename, typename, typename>
    struct objbind_result_type_helper;
    template <typename A0, typename RC, typename... RA, typename... A, typename...B>
    struct objbind_result_type_helper<true, A0, RC, type_list<RA...>, type_list<A...>, type_list<B...> > {
        typedef typename objbind_result_type<RC, type_list<RA..., A0>, type_list<A...>, type_list<B...> >::type type;
    };
    
    template <typename A0, typename RC, typename... RA, typename... A, typename...B>
    struct objbind_result_type_helper<false, A0, RC, type_list<RA...>, type_list<A...>, type_list<B...> > {
        typedef typename objbind_result_type<RC, type_list<RA...>, type_list<A...>, type_list<B...> >::type type;
    };
    
    template <typename RC,
              typename... RA,
              typename A0, typename... A,
              typename B0, typename... B>
    struct objbind_result_type<RC, type_list<RA...>,
                               type_list<A0, A...>,
                               type_list<B0, B...> > {
        typedef typename objbind_result_type_helper<bool(std::is_placeholder<B0>::value), A0,
                                                    RC, type_list<RA...>,
                                                    type_list<A...>,
                                                    type_list<B...> >::type type;
    };
    
    
    // This is the function I'd like to have working:
    template <class T, typename RETURN_TYPE, typename...Args, typename... Arguments>
    typename objbind_result_type<RETURN_TYPE, type_list<>, type_list<Args...>, type_list<Arguments...> >::type
    obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params)
    {
      return std::bind(in_fun, &obj, params...);
    }
    
    int main()
    {
      foo fo;
      auto fun = obj_bind(&foo::f, fo, _1, 2.34, _2);
      fun(17, 'b');
    }