Donotalo
Donotalo

Reputation: 13025

Array of class member functions in C++

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

Answers (2)

jignatius
jignatius

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);
}

Demo

Upvotes: 1

Miles Budnek
Miles Budnek

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

Related Questions