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?
At the line
traverse(&print);
, you are trying to pass a pointer to a member function to another member function. The correct syntax for this istraverse(&foo::print);
.The pointer to a member function is different from a pointer to a free function. It needs an instance to act with. This is missing in the
foo:traverse
function as well.The call to pointer to member function is tricky. This is also wrong in the code.
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 };
};