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:
CodePudding user response:
Your code has several issues:
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.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
.You only need to init the
Callbacks
once (probably in the contractor), and use it inrun
later.Upon passing the member function pointer, you need the syntax
&ClassName::MemberFunction
, which you have missed in the function callinit(5, {&fun1, &fun2}, 0);
.The
Base
required a virtual destructor for defined behavior, in case you store the children in theBase
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)
CodePudding user response:
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);
}
CodePudding user response:
@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.