Home > database >  Passing References to Member Functions
Passing References to Member Functions

Time:09-27

I've been working with a doubly-threaded BST in C , and I thought it would be cool to separate my visitor functions from my various traversals. However I can't figure out how to properly pass references to member functions into my traversal functions. Here is a massively simplified version of my problem:

class foo {
public:
    foo() {};
    ~foo() {};

    void print(int x) const { //visitor
        cout << x << endl;
    }

    void traverse(void (*visitor)(int)) { //traversal
        for (int i = 0; i < 9; i  )
            visitor(myAry[i]);
    }

    void printAll() { //function calling the traversal and passing it a reference to the visitor
        traverse(&print);
    }

    int myAry[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
};

The problem of course comes in the traverse(&print); statement.

Any clues what's going wrong, or what I could try differently to achieve the same effect?

CodePudding user response:

void (*visitor)(int)

In C this means: a pointer to a function that takes an int parameter and returns a void.

&print

The type of this expression is not "a pointer to a function that takes an int parameter and returns a void". It is "a pointer to a method of class foo that takes an int parameter and returns a void".

Class methods and functions are not the same thing. They might look the same, but they're not.

In your sample code you don't need to use a class method, for print, so just declare it as a static class member:

static void print(int x) const {
    cout << x << endl;
}

And, with no other changes, this should work, since this is now a function. The difference between a class method is a function is that a class method requires an object whose method gets invoked.

It's possible that your clear code does really require a pointer to a class method. In which case traverse() should probably be something like:

void traverse(void (*foo::visitor)(int)) {
    for (int i = 0; i < 9; i  )
        (this->*visitor)(myAry[i]);
}

and this would be invoked as

traverse(&foo::print);

This is because void (*foo::visitor)(int) means "a pointer to a method of class foo that takes an int parameter and returns a void". And this is what your print is.

CodePudding user response:

You must specify the class and which instance to call the function on. Also make sure that the signatures match.

void traverse(void(foo::*visitor)(int) const) {
//                 ^^^^^               ^^^^^
    for (int i = 0; i < 9; i  )
        (this->*visitor)(myAry[i]);
//       ^^^^^^
}

void printAll() {
    traverse(&foo::print);
//            ^^^^^
}

CodePudding user response:

Any clues what's going wrong, or what I could try differently to achieve the same effect?

However, the language updates makes it simpler nowadays. If you have access to C 20 or later, you can do via auto as function parameter type as follows:

#include <functional> // std::invoke
#include <array>      // std::array

struct foo 
{
    // ... code

    void traverse(auto visitor) // auto function paramet
    {
        for (const int i : myAry)
            std::invoke(visitor, this, i);
    }

    void printAll()
    {
        traverse(&foo::print); // pass the member function as 
    }
    // std::array instead of c-style arrays
    std::array<int, 10> myAry{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
};

See a Demo

  • Related