Функция для привязки функций-членов к экземплярам объекта в C++

В последнее время я часто привязывал функции-члены к экземплярам объектов. Вместо того, чтобы использовать комбинацию 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 на самом деле отлично работает с функциями-членами без аргументов, поэтому я почти уверен, что моя проблема заключается в том, как я с ними справляюсь, но после нескольких неудачных попыток исправить это я решил попробовать здесь за советом.


person Charles Ofria    schedule 07.12.2014    source источник
comment
Может быть, я что-то упускаю, но с std::bind вам не нужен std::mem_fn.   -  person Stephan Dollberg    schedule 08.12.2014
comment
Я могу подтвердить, что я только что удалил mem_fn, и он отлично работает на g++. Может быть, вы используете сломанную/старую библиотеку?   -  person Giulio Franco    schedule 08.12.2014
comment
Я не понимаю. Чем obj_bind существенно отличается от std::bind()? Вы можете просто сделать std::bind(&string::push_back, test_obj, 'C'), нет?   -  person Barry    schedule 08.12.2014
comment
Ах, я как-то упустил, что std::bind не требует std::mem_fn. Это сильно упростит меня. Что касается отличий от std::bind: std::ref по-прежнему необходим для привязки к ссылке целевого объекта.   -  person Charles Ofria    schedule 08.12.2014
comment
std::bind(&string::push_back, &test_obj, 'C') будет более близким аналогом, использование &test_obj эквивалентно std::ref(test_obj) для этой цели, это заставляет bind ссылаться на исходный объект, а не копировать его.   -  person Jonathan Wakely    schedule 08.12.2014


Ответы (1)


Прежде чем приступить к ремонту переплета, несколько замечаний:

  1. Вам действительно не нужно использовать std::mem_fn() для первого аргумента std::bind(), так как std::bind() уже знает, как обращаться с указателями на функции-члены.
  2. Тип получения адреса функции-члена не определен. То есть вы не можете на самом деле std::string::push_back быть указателем функции-члена, принимающим только один аргумент.
  3. Вместо использования std::ref(testobj) вы можете использовать только &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<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');
}
person Dietmar Kühl    schedule 07.12.2014
comment
Я понимаю. Я знал, что определение для std::bind должно быть сложным, но, учитывая, как сложно создать что-то, что просто использует динамическое связывание, это должно быть намного хуже, чем я ожидал. Однако, учитывая, что bind на самом деле делает то, что мне нужно, просто добавляя &, мне явно не нужна эта новая функция. Я очень ценю всю помощь. - person Charles Ofria; 08.12.2014
comment
Небольшое уточнение: тип получения адреса функции-члена типа стандартной библиотеки не определен. Вероятно, в большинстве случаев это нормально для пользовательских типов. - person Jonathan Wakely; 08.12.2014