Search code examples
c++visitor-patternfunction-reference

Passing References to Member Functions


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?


Solution

  • 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.