Подтвердить что ты не робот

Как написать функцию pointer-to-member с помощью std:: function?

Я знаю, как объявить int fn(double) внутри std:: function (std::function<int(double)>). Я знаю, как написать функцию-указатель-член (typedef int (A::*MemFn)(double d);). Но как написать функцию-указатель-член с помощью std:: function?

Манекен, если вы хотите компилировать/тестировать

-edit- на основе ответов. Думаю, я просто использую typedef и не беспокоюсь о std:: function

#include <cstdio>
#include <functional>

struct A{ int fn(double){ return 0; } };
int fn2(double){ return 0; }

typedef int (A::*MemFn)(double d);
typedef std::function<int(double)> MemFn2;

void Test(A*a, MemFn2 fn){
    fn(1.2f);
}
void Test(A*a, MemFn fn){
    (a->*fn)(1.2f);
}

int main(){
    Test(new A, &A::fn);
    Test(new A, &fn2);
}
4b9b3361

Ответ 1

std::function отлично умеет напрямую хранить указатель на функцию-член. Однако вам необходимо соответствующим образом скорректировать список аргументов. Указатели-члены должны вызываться с экземпляром типа (или производного типа). Если положить их в std::function, первый аргумент в списке аргументов должен быть указателем (или ссылкой или умным указателем) на тип объекта.

Итак, если у меня есть следующий класс:

struct Type
{
public:
    int Foo();
};

Правильный синтаксис для хранения этой функции-члена в std::function:

std::function<int(Type&)> fooCaller = &Type::Foo;

Если вы хотите сохранить список аргументов (в вашем случае int(double)), вам необходимо предоставить экземпляр вне function. Это можно сделать с помощью std::bind:

struct A{ int fn(double){ return 0; } };

A anInstance;
std::function<int(double)> fnCaller = std::bind(&A::fn, &anInstance, std::placeholders::_1);

Обратите внимание, что ваша ответственность гарантирует, что указатель объекта, который вы предоставляете std::bind, остается в живых, пока fnCaller жив. Если вы вернете fnCaller кому-то и указатель на объект стека, у вас проблемы.

Хорошо, что вы могли привязать shared_ptr (или любой скопируемый смарт-указатель) в качестве объекта, благодаря тому, как определяется механизм вызова функции:

struct A{ int fn(double){ return 0; } };

auto anInstance = std::make_shared<A>();
std::function<int(double)> fnCaller = std::bind(&A::fn, anInstance, std::placeholders::_1);

Теперь вам не нужно беспокоиться; связующее будет продолжать сохранять объект в живых, поскольку он сохраняет значение shared_ptr по значению.

Ответ 2

Функция-член не является функцией. Это не то, что вы можете назвать. Все, что вы можете сделать, это вызвать функцию-член объекта-экземпляра. Только пара функций-указателей и объектов представляет собой вызываемый объект.

Чтобы связать экземпляр с PTMF и получить что-то вызываемое, используйте bind:

#include <functional>

struct Foo
{
    double bar(bool, char);
};

Foo x;
using namespace std::placeholders;
std::function<double(bool, char)> f = std::bind(&Foo::bar, x, _1, _2);
f(true, 'a'); //...

Как и в случае с lambdas, выражения bind имеют непознаваемый тип, а преобразование в std::function (а также фактическая отправка) является потенциально дорогостоящим. Если возможно, предпочтительно использовать auto для типа выражения привязки.

Ответ 3

Одно из рекомендаций в книге Скотта Мейера Modern С++ 11 заключается в том, чтобы избежать std::bind и всегда использовать лямбда-закрытие:

struct A{ int fn(double){ return 0; } };

std::function<int(double)> f = [a = A{}](double x) mutable { return a.fn(x); };

Здесь требуется mutable, так как захват a может быть потенциально изменен вызовом функции (поскольку A::fn не является константой).

Ответ 4

Вы можете использовать std::binder1st для привязки функции-члена к экземпляру класса:

typedef std::binder1st<std::mem_fun1_t<int, A, double>> MemFn;

void Test(A* a, double d)
{
   MemFn fn(std::mem_fun(&A::fn), a);
   int nRetVal = fn(d);
}

int main()
{
   Test(new A, 1.2f);
   return 0;
}

Ответ 5

Если вы можете использовать Boost, вы можете использовать Boost.Bind. Он легко выполняется следующим образом:

boost::bind(&MyClass::MemberFunction, pInstance, _1, _2)

Надеюсь, это довольно понятно. _1 и _2 являются заполнителями для параметров, которые вы можете передать в функцию.