简体   繁体   中英

Avoid the class scope so as to pass a member function as a function pointer

I'll describe my question using the following sample code.

I have class B defined as follows:

class B
{
    public:
        inline B(){}
        inline B(int(*f)(int)) :myfunc{ f }{}
        void setfunction(int (*f)(int x)) { myfunc = f; }
        void print(int number) { std::cout << myfunc(number) << std::endl; }
    private:
        int(*myfunc)(int);
};

I then define class A as follows:

class A
{
    public:
    A(int myint) :a{ myint }{ b.setfunction(g); }
    int g(int) { return a; }
    void print() { b.print(a); }

   private:
   B b;
   int a;
   };

To me the issue seems to be that the member function g has the signature int A::g(int) rather than int g(int) .

Is there a standard way to make the above work? I guess this is quite a general setup, in that we have a class (class B) that contains some sort of member functions that perform some operations, and we have a class (class A) that needs to use a particular member function of class B -- so is it that my design is wrong, and if so whats the best way to express this idea?

You can use std::function:

class B
{
public:
  inline B() {}
  inline B(std::function<int(int)> f) : myfunc{ f } {}
  void setfunction(std::function<int(int)> f) { myfunc = f; }
  void print(int number) { std::cout << myfunc(number) << std::endl; }
private:
  std::function<int(int)> myfunc;
};

class A
{
public:
  A(int myint) :a{ myint } {
    b.setfunction([this](int a) {
      return g(a);
    }
    );
  }
  int g(int) { return a; }
  void print() { b.print(a); }

private:
  B b;
  int a;
};

You could generalize the class B . Instead of keeping a pointer ( int(*)(int) ), what you really want is any thing that I can call with an int and get back another int . C++11 introduced a type-erased function objection for exactly this reason: std::function<int(int)> :

class B
{
    using F = std::function<int(int)>
public:
    B(){}
    B(F f) : myfunc(std::move(f)) { }
    void setfunction(F f) { myfunc = std::move(f); }
    void print(int number) { std::cout << myfunc(number) << std::endl; }
private:
    F myfunc;
};

And then you can just provide a general callable into B from A :

A(int myint)
: b([this](int a){ return g(a); })
, a{ myint }
{ }

Use std::function and std::bind

class B
{
    public:
        inline B(int(*f)(int)) :myfunc{ f }{}
        void setfunction(std::function<int(int)> f) { myfunc = f; }
        void print(int number) { std::cout << myfunc(number) << std::endl; }
    private:
        std::function<int(int)> myfunc;
};

// ...
A a;
B b(std::bind(&A::g, &a));

Also note that you should initialize the function pointer to some default value (most likely null) and check for it when using, otherwise it's value is undefined.

You could use std::bind to bind the member function A::g .

class B
{
    public:
        inline B(){}
        inline B(std::function<int(int)> f) :myfunc{ f }{}
        void setfunction(std::function<int(int)> f) { myfunc = f; }
        void print(int number) { std::cout << myfunc(number) << std::endl; }
    private:
        std::function<int(int)> myfunc;
};
class A
{
    public:
        A(int myint) :a{ myint } { 
            b.setfunction(std::bind(&A::g, this, std::placeholders::_1)); 
        }
        int g(int) { return a; }
        void print() { b.print(a); }

    private:
        B b;
        int a;
};

Note you need to change the type of functor from function pointer to std::function , which is applicable with std::bind .

LIVE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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