How can I pass a member function where a free func

2019-01-01 14:51发布

The question is the following: consider this piece of code:

#include <iostream>


class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a();

    function1(&test);
    function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?

    return 0;
}

How can I use the a's aClass::test as an argument to function1? I'm stuck in doing this.

I would like to access a member of the class.

7条回答
爱死公子算了
2楼-- · 2019-01-01 15:07

There isn't anything wrong with using function pointers. However, pointers to non-static member functions are not like normal function pointers: member functions need to be called on an object which is passed as an implicit argument to the function. The signature of your member function above is, thus

void (aClass::*)(int, int)

rather than the type you try to use

void (*)(int, int)

One approach could consist in making the member function static in which case it doesn't require any object to be called on and you can use it with the type void (*)(int, int).

If you need to access any non-static member of your class and you need to stick with function pointers, e.g., because the function is part of a C interface, your best option is to always pass a void* to your function taking function pointers and call your member through a forwarding function which obtains an object from the void* and then calls the member function.

In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types. If using a templated interface is undesirable you should use something like std::function<void(int, int)>: you can create a suitably callable function object for these, e.g., using std::bind().

The type-safe approaches using a template argument for the class type or a suitable std::function<...> are preferable than using a void* interface as they remove the potential for errors due to a cast to the wrong type.

To clarify how to use a function pointer to call a member function, here is an example:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, 0);
    foo object;
    somefunction(&forwarder, &object);
}
查看更多
倾城一夜雪
3楼-- · 2019-01-01 15:13

@Pete Becker's answer is fine but you can also do it without passing the class instance as an explicit parameter to function1 in C++ 11:

#include <functional>
using namespace std::placeholders;

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}
查看更多
路过你的时光
4楼-- · 2019-01-01 15:19

I asked a similar question (C++ openframeworks passing void from other classes) but the answer I found was clearer so here the explanation for future records:

it’s easier to use std::function as in:

 void draw(int grid, std::function<void()> element)

and then call as:

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

or even easier:

  grid.draw(12, [&]{a.draw()});

where you create a lambda that calls the object capturing it by reference

查看更多
高级女魔头
5楼-- · 2019-01-01 15:22

Since 2011, if you can change function1, do so, like this:

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

(live demo)

Notice also that I fixed your broken object definition (aClass a(); declares a function).

查看更多
美炸的是我
6楼-- · 2019-01-01 15:24

A pointer to member function is different from a pointer to function. In order to use a member function through a pointer you need a pointer to it (obviously ) and an object to apply it to. So the appropriate version of function1 would be

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

and to call it:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);
查看更多
无色无味的生活
7楼-- · 2019-01-01 15:28

I made the member function as static and all works:

#include <iostream>

class aClass
{
public:
    static void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

void function1(int a,int b,void function(int, int))
{
    function(a, b);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(10,12,test);
    function1(10,12,a.aTest); // <-- How should I point to a's aClass::test function?

    getchar();return 0;
}
查看更多
登录 后发表回答