简体   繁体   English

指向成员函数的函数指针

[英]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"在这个例子中,我希望输出为“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?为什么?

The syntax is wrong.语法错误。 A member pointer is a different type category from a ordinary pointer.成员指针是与普通指针不同的类型类别。 The member pointer will have to be used together with an object of its class:成员指针必须与其类的对象一起使用:

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
}

ax does not yet say on what object the function is to be called on. ax尚未说明要调用该函数的对象。 It just says that you want to use the pointer stored in the object a .它只是说您想使用存储在对象a的指针。 Prepending a another time as the left operand to the .* operator will tell the compiler on what object to call the function on. a另一个时间作为.*运算符的左操作数将告诉编译器在哪个对象上调用该函数。

int (*x)() is not a pointer to member function. int (*x)()不是指向成员函数的指针。 A pointer to member function is written like this: int (A::*x)(void) = &A::f;指向成员函数的指针是这样写的: int (A::*x)(void) = &A::f; . .

Call member function on string command 在字符串命令上调用成员函数

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

Pay attention to (this->*func)();注意(this->*func)(); and the way to declare the function pointer with class name void (A::*func)()以及用类名声明函数指针的方法void (A::*func)()

You need to use a pointer to a member function, not just a pointer to a function.您需要使用指向成员函数的指针,而不仅仅是指向函数的指针。

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;
}

While you unfortunately cannot convert an existing member function pointer to a plain function pointer, you can create an adapter function template in a fairly straightforward way that wraps a member function pointer known at compile-time in a normal function like this:虽然遗憾的是您无法将现有成员函数指针转换为普通函数指针,但您可以以相当简单的方式创建一个适配器函数模板,将编译时已知的成员函数指针包装在一个普通函数中,如下所示:

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.请注意,为了调用成员函数,必须提供A的实例。

While this is based on the sterling answers elsewhere on this page, I had a use case which wasn't completely solved by them;虽然这是基于本页其他地方的标准答案,但我有一个用例并没有完全被他们解决; for a vector of pointers to functions do the following:对于指向函数的指针向量,请执行以下操作:

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

Something like this is useful if you are writing a command interpreter with indexed functions that need to be married up with parameter syntax and help tips etc. Possibly also useful in menus.如果您正在编写一个带有索引函数的命令解释器,这些函数需要与参数语法和帮助提示等结合起来,那么这样的东西很有用。 可能在菜单中也很有用。

Building on @IllidanS4 's answer, I have created a template class that allows virtually any member function with predefined arguments and class instance to be passed by reference for later calling.以@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)...)) {}
};

Test / example:测试/示例:

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
}

Obviously, this will only work if the given arguments and owner class are still valid.显然,这只有在给定的参数和所有者类仍然有效时才有效。 As far as readability... please forgive me.至于可读性......请原谅我。

Edit: removed unnecessary malloc by making the tuple normal storage.编辑:通过使元组正常存储删除了不必要的 malloc。 Added inherited type for the reference.添加了引用的继承类型。 Added option to provide all arguments at calltime instead.添加了在调用时提供所有参数的选项。 Now working on having both....现在正在努力同时拥有......

Edit 2: As promised, both.编辑2:正如承诺的那样,两者都是。 Only restriction (that I see) is that the predefined arguments must come before the runtime supplied arguments in the callback function.唯一的限制(我看到的)是预定义的参数必须出现在回调函数中运行时提供的参数之前。 Thanks to @Chipster for some help with gcc compliance.感谢@Chipster 对 gcc 合规性的一些帮助。 This works on gcc on ubuntu and visual studio on windows.这适用于 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)...);
}

Edit 3: clang compliance, greater flexibility and examples.编辑 3: clang 合规性,更大的灵活性和示例。 (Ripped from my active hobby project, which I plan to open source... eventually.) (从我活跃的爱好项目中提取出来,我计划开源......最终。)

//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
}



While writing this up, I ran into libstdc++ known bug #71096 which breaks std::get when >1 argument is given at callback construction time.在写这篇文章时,我遇到了libstdc++ 已知错误 #71096 ,当在回调构造时给出 >1 参数时,它会破坏std::get This bug has been marked fix in gcc 11, which has unfortunately not made it into the ubuntu repo at this time (apt says I'm up to date with 9.3.0)此错误已在 gcc 11 中标记为修复,遗憾的是此时尚未将其纳入 ubuntu 存储库(apt 说我是最新的 9.3.0)

@Johannes Schaub - litb has the correct solution, but I thought it would be beneficial to post a generic example of using a pointer to a member function as well. @Johannes Schaub - litb有正确的解决方案,但我认为发布一个使用指向成员函数的指针的通用示例也是有益的。

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

C++17 has a template function for calling a pointer to a member function, which looks like this. C++17有一个模板函数,用于调用成员函数的指针,看起来像这样。

std::invoke(memberFunctionPointer, myString);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM