1

I have a base class with common behavior and derived classes with override virtual functions. But if tried to save the function pointer as std::vector I will get the error:

 ISO C++ forbids taking the address of an unqualified or
  parenthesized non-static member function to form a pointer to member function.

I need something like this:

class Base
{
public:
    virtual int fun1(int inp) = 0;
    virtual int fun2(int inp) = 0;
    int init(int inp, std::vector<std::function<int(int)>> Callbacks, int index)
    {
        if (index == 0)
            return Callbacks[0](inp);
        else
            return Callbacks[1](inp);
    }
    int run()
    {
        return init(5, { &fun1, &fun2 }, 0);
    }
};

class A : public Base
{
    int fun1(int inp)
    {
        return inp * 10;
    }
    int fun1(int inp)
    {
        return inp * 100;
    }
};

class B : public Base
{
    int fun1(int inp)
    {
        return inp * 20;
    }
    int fun2(int inp)
    {
        return inp * 200;
    }
};

int main(int argc, char const* argv[])
{
    auto f = new B;
    int e = f->run();
    return 0;
}

Is there any way to do what I want? Maybe I can bind a virtual function to a container somehow? Or maybe can I set the lambda function as virtual? When I tried to declare lambda function in the Base class when I change function in derived class I get errors:

JeJo
  • 30,635
  • 6
  • 49
  • 88
Wusiki Jeronii
  • 159
  • 1
  • 8
  • 1
    *ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.* -- I think you need to figure out this error first and understand what it's telling you. Pointers to member functions are not the same as pointers to non-member, or static, or "free" functions. – PaulMcKenzie Sep 15 '21 at 12:41
  • @PaulMcKenzie I understand why I get this error. fun1 & fun2 are non-static functions. But I can't declare these ones as static 'cos virtual functions can not be static. – Wusiki Jeronii Sep 15 '21 at 12:43
  • How do you declare a pointer to a non-static member function, and then call it via pointer? That's the point I'm making -- accomplish that first, and things will get clearer. – PaulMcKenzie Sep 15 '21 at 12:45
  • A virtual function is just a placeholder. You need to define it in a derived class for the compiler to get its address. Thats why a virtual function cannot be stored in an array. – moi Sep 15 '21 at 12:45

3 Answers3

3

Your code has several issues:

  1. Pointers to member functions are not the same as pointers to non-member, or static, or "free" functions. That means the type here std::function<int(int)> is wrong as it meant for free functions or static functions.

  2. The call to the member function pointer is not the same as the normal function call. You need to specify the object to which you are calling the member function pointer. Since C++17 you can also invoke the pointer to member function using more generic std::invoke.

  3. You only need to init the Callbacks once (probably in the contractor), and use it in run later.

  4. Upon passing the member function pointer, you need the syntax &ClassName::MemberFunction, which you have missed in the function call init(5, {&fun1, &fun2}, 0);.

  5. The Base required a virtual destructor for defined behavior, in case you store the children in the Base class pointer at a later point.

Following is the example code as per the above changes:

#include <functional> // std::invoke

class Base
{
    // member function pointer type
    using MemFunPtrType = int(Base::*)(int);
    // to store the member functions pointers
    std::vector<MemFunPtrType> mMemberFunctionPts; 

public:
    Base() // initlize the mMemberFunctionPts once!
        : mMemberFunctionPts{ { &Base::fun1, &Base::fun2 } }
    {}
    virtual int fun1(int inp) = 0;
    virtual int fun2(int inp) = 0;
    virtual ~Base() = default;

    // run choose the correct member function as per the passed index!
    int run(std::size_t index)
    {
        if (2u == mMemberFunctionPts.size() && index == 0)
            return (this->*mMemberFunctionPts[index])(5);
            // or using std::invoke
            // return std::invoke(mMemberFunctionPts[index], this, 5);
        else
            return (this->*mMemberFunctionPts[index])(5);
    }
};

See a (Live Demo Here)

JeJo
  • 30,635
  • 6
  • 49
  • 88
1

You get a "pointer" to a member with the syntax &Class::Member (Class::Member is the "qualified" name of the member).

These are pointers only in an abstract sense, and need to be dereferenced relative to an object.
You do this with the ->* or .* operator, depending on whether the left-hand side is a pointer or not.
Note that you must use this explicitly, if that is the relevant object.

The type of &Base::fun1 and &Base::fun2 is int (Base::*)(int), which can't be converted to std::function<int(int)>.

Putting it together:

int init(int inp, std::vector<int (Base::*)(int)> Callbacks, int index)
{
    if (index == 0)
        return (this->*Callbacks[0])(inp); // The leftmost parentheses are necessary.
    else
        return (this->*Callbacks[1])(inp);
}

int run()
{
    return init(5, {&Base::fun1, &Base::fun2}, 0);
}

If you don't want to limit the callbacks to members, you can use lambda functions, capturing this:

int init(int inp, std::vector<std::function<int(int))> Callbacks, int index)
{
    if (index == 0)
        return Callbacks[0](inp);
    else
        return Callbacks[1](inp);
}

int run()
{
    return init(5, 
                {[this](int i) { return fun1(i); },
                 [](int) { return 567; } }, 
                0);
}
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

@PaulMcKenzie, thanks. Now it works:

class Base
{
public:
    std::function<int(int)> fun1;
    std::function<int(int)> fun2;
    int init(int inp, std::vector<std::function<int(int)>> Callbacks, int index)
    {
        if (index == 0)
            return Callbacks[0](inp);
        else
            return Callbacks[1](inp);
    }

    int run()
    {
        return init(5, {&fun1, &fun2}, 0);
    }
};

class B : public Base
{
public:
    B()
    {
        fun1 = fun1B;
        fun2 = fun2B;
    }

private:
    std::function<int(int)> fun1B = [&](int inp)
    {
        return inp * 20;
    };
    std::function<int(int)> fun2B = [&](int inp)
    {
        return inp * 200;
    };
};

int main(int argc, char const *argv[])
{
    auto f = new B();
    int e = f->run();
    assert(e == 5*20);
    return 0;
}

But the example above looks more correct.

Wusiki Jeronii
  • 159
  • 1
  • 8