Reputation: 13025
I'm using C++17
. I'm trying to store class member functions in a vector
. I've a class where I've several functions with same signature. My scenario is to store them in a vector
and execute one by one. Consider this example:
class Test
{
int Func(const string& input, int index)
{
return 0;
}
public:
void Execute()
{
vector<function<int(const string&, int)>> exes;
exes.push_back(Test::Func);
}
};
int main()
{
Test test;
test.Execute();
cout << "Hello World!" << endl;
return 0;
}
When I try to compile it, I get the following error:
error: invalid use of non-static member function 'int Test::Func(const string&, int)'
exes.push_back(Test::Func);
~~~~~~^~~~
main.cc" data-line="13" data-column="9">main.cc:13:9: note: declared here
int Func(const string& input, int index)
How can I achieve this in C++17
?
Upvotes: 2
Views: 252
Reputation: 6484
Non-static member functions of a class always pass the this
pointer implicitly as an argument. Hence the compiler error. You would have to bind the member function to an instance of a class in order to pass the this
pointer.
In your case, if you have a class like:
class MyClass
{
public:
int Func(const std::string& input, int index)
{
std::cout << input << " " << index << std::endl;
return 0;
}
};
you can add function objects to your vector like this using std::bind
or a lambda:
std::vector<std::function<int(const std::string&, int)>> exes;
MyClass obj;
//Using std::bind
exes.push_back(std::bind(&MyClass::Func, &obj, std::placeholders::_1, std::placeholders::_2));
//Or a lambda
exes.push_back([&obj](const std::string& str, int n) { return obj.Func(str, n); });
Then you can call your function objects with parameters:
for (auto &fn : exes)
{
fn("hello", 1);
}
Upvotes: 1
Reputation: 30569
Non-static class member functions aren't like freestanding functions and static member functions. They require an object to work against: the implicit this
pointer; the thing to the left of the .
(or ->
) when calling them normally. That is, the type of &Test::Func
isn't int(*)(const std::string&, int)
, it's int(Test::*)(const std::string&, int)
.
That means that Test::Func
isn't compatible with std::function<int(const string&, int)>
. What it is compatible with is std::function<int(Test&, const std::string&, int)>
or std::function<int(Test*, const std::string&, int)>
. std::function
will take care of the special invocation needed to call a pointer-to-member-function for you and will do the right thing based on the type of its first parameter (either a pointer or a reference).
For example, the following will work fine:
std::function<int(Test&, const std::string&, int)> func{&Test::Func};
Test foo;
std::cout << func(foo, "hello", 42) << '\n';
If you don't want to have to supply a Test
object at the call site, you can either make Func
static
, in which case it will behave exactly like a freestanding function, or use a lambda or std::bind
to make std::function
wrap a function-like object that stores a pointer or reference back to a Test
instance or an entire Test
instance:
Test foo;
std::function<int(const std::string&, int)> func{
[&foo](const std::string& str, int i) {
return foo.Func(str, i);
}
};
// func holds a reference to foo, so no need to pass it
std::cout << func("hello", 42) << '\n';
Be careful doing this though. If you capture a reference or pointer to an object that object must still be alive when the std::function
wrapper is called.
Upvotes: 5