【问题标题】:Function to bind member functions to object instances in C++将成员函数绑定到 C++ 中的对象实例的函数
【发布时间】:2023-03-11 20:05:01
【问题描述】:

最近,我经常将成员函数绑定到对象实例。我不想使用 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 函数实际上在没有参数的成员函数上运行良好,所以我很确定我的问题在于我如何处理这些,但是在尝试修复它几次失败后,我想我会在这里尝试建议。

【问题讨论】:

  • Mabye 我遗漏了一些东西,但使用std::bind 你不需要std::mem_fn
  • 我可以确认我刚刚删除了 mem_fn,它在 g++ 上运行良好。也许您正在使用损坏/旧的库?
  • 我不明白。 obj_bindstd::bind() 有何明显不同?你可以std::bind(&amp;string::push_back, test_obj, 'C'),不是吗?
  • 啊——我不知何故错过了std::bind 不需要std::mem_fn。这将为我做很多简化。至于与std::bind 的区别:std::ref 仍然需要绑定到目标对象的引用。
  • std::bind(&amp;string::push_back, &amp;test_obj, 'C') 是一个更接近的类比,为此使用 &amp;test_obj 等效于 std::ref(test_obj),它会导致 bind 引用原始对象而不是复制它

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


【解决方案1】:

在开始修复活页夹之前,请注意几点:

  1. 您实际上不需要将std::mem_fn() 用于std::bind() 的第一个参数,因为std::bind() 已经知道如何处理成员函数指针。
  2. 获取成员函数地址的类型未定义。也就是说,您不能真正将 std::string::push_back 视为只接受一个参数的成员函数指针。
  3. 你可以直接使用&amp;testobj,而不是std::ref(testobj)

如果成员函数需要有参数,则不能像这样推断它:您需要指定参数:

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) {
    ...
}

这可以帮助您克服直接错误。下一个问题是您以某种方式将 bound 参数与被调用的函数类型相关联。当然,这不是它的工作方式。在您的情况下,结果参数实际上不带任何参数,即,您将有这样的声明:

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&lt;RC(...)&gt; 实际上会带一些参数。弄清楚这些论点有点不重要。但是,当将参数类型限制为指向函数的指针或指向成员函数的指针时,应该可以返回适当的函数对象。

只是为了好玩,这里有一个似乎处理占位符的实现(虽然它没有经过彻底测试):

#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');
}

【讨论】:

  • 我明白了。我知道std::bind 的定义需要很复杂,但考虑到构建仅使用动态绑定的东西是多么困难,它一定比我预期的要糟糕得多。但是,鉴于 bind 实际上只需要添加一个 & 就可以满足我的需要,我显然不需要这个新功能。我非常感谢所有的帮助。
  • 小说明:获取标准库类型成员函数地址的类型未定义。在大多数情况下,这对于用户定义的类型可能没问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
  • 1970-01-01
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
相关资源
最近更新 更多