Как написать функцию 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);
}
Ответы
Ответ 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
являются заполнителями для параметров, которые вы можете передать в функцию.