Function pointer to member function

I'd like to set up a function pointer as a member of a class that is a pointer to another function in the same class. The reasons why I'm doing this are complicated.

In this example, I would like the output to be "1"

class A {
public:
int f();
int (*x)();
}


int A::f() {
return 1;
}




int main() {
A a;
a.x = a.f;
printf("%d\n",a.x())
}

But this fails at compiling. Why?

169365 次浏览

语法错了。成员指针是与普通指针不同的类型类别。成员指针必须与其类的一个对象一起使用:

class A {
public:
int f();
int (A::*x)(); // <- declare by saying what class it is a pointer to
};


int A::f() {
return 1;
}




int main() {
A a;
a.x = &A::f; // use the :: syntax
printf("%d\n",(a.*(a.x))()); // use together with an object of its class
}

a.x还没有说明要对哪个对象调用函数。它只是说您希望使用存储在对象 a中的指针。另一次将 a作为 .*操作符的左操作数,将告诉编译器调用函数的对象。

您需要使用指向成员函数的指针,而不仅仅是指向函数的指针。

class A {
int f() { return 1; }
public:
int (A::*x)();


A() : x(&A::f) {}
};


int main() {
A a;
std::cout << (a.*a.x)();
return 0;
}

int (*x)()不是指向成员函数的指针。指向成员函数的指针是这样写的: int (A::*x)(void) = &A::f;

调用字符串命令的成员函数

#include <iostream>
#include <string>




class A
{
public:
void call();
private:
void printH();
void command(std::string a, std::string b, void (A::*func)());
};


void A::printH()
{
std::cout<< "H\n";
}


void A::call()
{
command("a","a", &A::printH);
}


void A::command(std::string a, std::string b, void (A::*func)())
{
if(a == b)
{
(this->*func)();
}
}


int main()
{
A a;
a.call();
return 0;
}

注意 (this->*func)();以及如何声明类别为 void (A::*func)()的函数指针

虽然这是基于这个页面其他地方的正确答案,但是我有一个用例没有完全被他们解决; 对于函数的向量指针来说,可以执行以下操作:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>


class A{
public:
typedef vector<int> (A::*AFunc)(int I1,int I2);
vector<AFunc> FuncList;
inline int Subtract(int I1,int I2){return I1-I2;};
inline int Add(int I1,int I2){return I1+I2;};
...
void Populate();
void ExecuteAll();
};


void A::Populate(){
FuncList.push_back(&A::Subtract);
FuncList.push_back(&A::Add);
...
}


void A::ExecuteAll(){
int In1=1,In2=2,Out=0;
for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){
Out=(this->*FuncList[FuncId])(In1,In2);
printf("Function %ld output %d\n",FuncId,Out);
}
}


int main(){
A Demo;
Demo.Populate();
Demo.ExecuteAll();
return 0;
}

如果您正在编写一个带有索引函数的命令解释器,需要结合参数语法和帮助提示等,那么这样的东西是非常有用的。可能在菜单中也有用。

虽然不幸的是,你不能将现有的成员函数指针转换成普通的函数指针,但是你可以用一种相当简单的方法来创建一个适配器函数模板,这种方法可以将一个在编译时已知的成员函数指针包装在一个普通函数中,如下所示:

template <class Type>
struct member_function;


template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...)>
{
template <Ret(Type::*Func)(Args...)>
static Ret adapter(Type &obj, Args&&... args)
{
return (obj.*Func)(std::forward<Args>(args)...);
}
};


template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...) const>
{
template <Ret(Type::*Func)(Args...) const>
static Ret adapter(const Type &obj, Args&&... args)
{
return (obj.*Func)(std::forward<Args>(args)...);
}
};

 

int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;

Note that in order to call the member function, an instance of A must be provided.

基于@IllidanS4的回答,我创建了一个模板类,它允许任何具有预定义参数和类实例的成员函数通过引用传递,以便以后调用。




template<class RET, class... RArgs> class Callback_t {
public:
virtual RET call(RArgs&&... rargs) = 0;
//virtual RET call() = 0;
};


template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
T * owner;
RET(T::*x)(RArgs...);
RET call(RArgs&&... rargs) {
return (*owner.*(x))(std::forward<RArgs>(rargs)...);
};
CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};


template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
T* owner;
RET(T::*x)(Args...);
RET call() {
return (*owner.*(x))(std::get<Args&&>(args)...);
};
std::tuple<Args&&...> args;
CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};

测试/示例:

class container {
public:
static void printFrom(container* c) { c->print(); };
container(int data) : data(data) {};
~container() {};
void print() { printf("%d\n", data); };
void printTo(FILE* f) { fprintf(f, "%d\n", data); };
void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
int data;
};


int main() {
container c1(1), c2(20);
CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
Callback_t<void>* fp1 = &f1;
fp1->call();//1
CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
Callback_t<void>* fp2 = &f2;
fp2->call();//20
CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
Callback_t<void, int>* fp3 = &f3;
fp3->call(15);//20:15
}

显然,这只有在给定的参数和所有者类仍然有效的情况下才会有效。至于可读性... 请原谅我。

编辑: 通过使元组正常存储消除了不必要的 malloc。为引用添加了继承类型。增加了在调用时提供所有参数的选项。现在两者兼备。

编辑2: 正如承诺的那样,两者。唯一的限制(我看到的)是,预定义的参数必须在运行时在回调函数中提供参数之前出现。感谢@Chipster 在遵守 gcc 方面提供的帮助。这个工作在 ubuntu 的 gcc 和 windows 的 Visual Studio 上。

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif


template<class RET, class... RArgs> class Callback_t {
public:
virtual RET call(RArgs... rargs) = 0;
virtual ~Callback_t() = default;
};


template<class RET, class... RArgs> class CallbackFactory {
private:
template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
private:
T * owner;
RET(T::*x)(CArgs..., RArgs...);
std::tuple<CArgs...> cargs;
RET call(RArgs... rargs) {
return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
};
public:
Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
~Callback() {};
};
public:
template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}

编辑3: 叮当依从性,更大的灵活性和例子。(摘自我的业余爱好项目,我计划最终开放源代码... ...)

//CallbackFactory.h
#pragma once


#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif


namespace WITE {


template<class RET, class... RArgs> class Callback_t {
public:
virtual RET call(RArgs... rargs) const = 0;
virtual ~Callback_t() = default;
};


template<class RET, class... RArgs> class CallbackFactory {
private:
template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
private:
RET(T::*x)(CArgs..., RArgs...);
T * owner;
std::tuple<CArgs...> cargs;
public:
Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
~Callback() {};
RET call(RArgs... rargs) const override {
return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
};
};
template<class... CArgs> class StaticCallback : public Callback_t<RET, RArgs...> {
private:
RET(*x)(CArgs..., RArgs...);
std::tuple<CArgs...> cargs;
public:
StaticCallback(RET(*x)(CArgs..., RArgs...), CArgs... pda);
~StaticCallback() {};
RET call(RArgs... rargs) const override {
return (*x)(std::get<CArgs>(cargs)..., rargs...);
};
};
public:
typedef Callback_t<RET, RArgs...>* callback_t;
template<class U, class... CArgs> static callback_t make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
template<class... CArgs> static callback_t make(CArgs... cargs, RET(*func)(CArgs..., RArgs...));//for non-members or static members
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2>
CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) :
x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}


template<class RET2, class... RArgs2> template<class... CArgs2>
CallbackFactory<RET2, RArgs2...>::StaticCallback<CArgs2...>::StaticCallback(RET2(*x)(CArgs2..., RArgs2...), CArgs2... pda) :
x(x), cargs(std::forward<CArgs2>(pda)...) {}


template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>*
CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
};


template<class RET, class... RArgs> template<class... CArgs> Callback_t<RET, RArgs...>*
CallbackFactory<RET, RArgs...>::make(CArgs... cargs, RET(*func)(CArgs..., RArgs...)) {
return new wintypename CallbackFactory<RET, RArgs...>::StaticCallback<CArgs...>(func, std::forward<CArgs>(cargs)...);
};


#define typedefCB(name, ...) typedef WITE::CallbackFactory<__VA_ARGS__> name## _F; typedef typename name## _F::callback_t name ;


typedefCB(rawDataSource, int, void*, size_t)


};


//example:
class Integer {
public:
typedefCB(oneInOneOut, int, int);
typedefCB(twoInOneOut, int, int, int);
int value;
Integer(int v) : value(v) {};
int plus(int o) {
return value + o;
};
int plus(int a, int b, int c) {
return value + a + b + c;
};
static int simpleSum(int a, int b) {
return a + b;
};
};


int main(int argc, char** argv) {
Integer::twoInOneOut sumOfTwo = Integer::twoInOneOut_F::make(&Integer::simpleSum);
std::cout << sumOfTwo->call(5, 6) << std::endl;//11
//
Integer seven(7);
Integer::oneInOneOut sevenPlus = Integer::oneInOneOut_F::make<Integer>(&seven, &Integer::plus);
std::cout << sevenPlus->call(12) << std::endl;//19
//
Integer::twoInOneOut seventeenPlus = Integer::twoInOneOut_F::make<Integer, int>(&seven, 10, &Integer::plus);//provide the 1st arg here, and the other 2 when called
std::cout << seventeenPlus->call(52, 48) << std::endl;//117
}






在写这篇文章的时候,我遇到了 Libstdc + + 已知 bug # 71096,当在回调构造时给出 > 1个参数时,它会破坏 std::get。这个 bug 已经在 gcc11中被标记为补丁,不幸的是目前还没有进入 ubunturepo (apt 说我最新的版本是9.3.0)

@ Johannes Schaub-litb 有正确的解决方案,但是我认为发布一个使用成员函数指针的通用示例也是有益的。

std::string myString{ "Hello World!" };
auto memberFunctionPointer{ &std::string::length };
auto myStringLength{ (myString.*memberFunctionPointer)() };

C + + 17有一个模板函数,用于调用指向成员函数的指针,如下所示。

std::invoke(memberFunctionPointer, myString);