I have a function object definition:
struct BaseFunctor
{
std::string desc = "Not this one!";
virtual double operator()(double a, double (*func) (double)) = 0;
};
and a set of derived function object definitions:
struct DerivedFunctor1 : public BaseFunctor
{
std::string desc = "Yes this!";
virtual double operator()(double a, double (*func) (double))
{
return a * func(a);
}
};
struct DerivedFunctor2 : public BaseFunctor
{
std::string desc = "This is also correct!";
virtual double operator()(double a, double (*func) (double))
{
return 5 * a * func(a);
}
};
They are instantiated and used in the following way:
double f1(double x){
return x*x+x;
}
template <typename T, typename F>
void do_something(T &func, F &derived)
{
double a = 1.0;
double res = derived(a, func);
std::cout << derived.desc << std::endl;
std::cout << "Result is: " << res << std::endl;
}
int main()
{
std::vector<BaseFunctor*> functors;
DerivedFunctor1 *derived1 = new DerivedFunctor1;
DerivedFunctor2 *derived2 = new DerivedFunctor2;
functors.push_back(derived1);
functors.push_back(derived2);
for (auto &f : functors)
{
do_something(f1, *f);
}
}
Now, the reason that the two function objects are derived from BaseFunctor was so I could collect them in a standard container and iterate through them. Are there other and more efficient ways to iterating through function objects?
Secondly, running the code outputs
Not this one!
Result is: 2
Not this one!
Result is: 10
When I try to access the member variable desc
, I get the member variable of the parent class. I could write getters and get access to the member variables of the derived function objects in that way but that seems like a lot of work if there are many member variables to the function objects. Are there any other way of achieving this?
Your derived classes define another member named desc
in addition to existing BaseFunctor::desc
.
What you rather need is to initialize BaseFunctor::desc
with the correct string. Example:
#include <iostream>
#include <memory>
#include <vector>
struct BaseFunctor {
std::string const desc;
virtual double operator()(double a, double (*func) (double)) = 0;
virtual ~BaseFunctor() noexcept = default;
protected:
BaseFunctor(std::string desc) noexcept
: desc(move(desc))
{}
};
struct DerivedFunctor1 : public BaseFunctor {
DerivedFunctor1() : BaseFunctor("Yes this!") {}
double operator()(double a, double (*func) (double)) override { return a * func(a); }
};
struct DerivedFunctor2 : public BaseFunctor {
DerivedFunctor2() : BaseFunctor("This is also correct!") {}
double operator()(double a, double (*func) (double)) override { return 5 * a * func(a); }
};
template <typename T>
void do_something(T &func, BaseFunctor &derived) {
double a = 1.0;
double res = derived(a, func);
std::cout << derived.desc << '\n';
std::cout << "Result is: " << res << '\n';
}
double f1(double a) noexcept { return a * a + a; }
int main() {
using P = std::unique_ptr<BaseFunctor>;
std::vector<P> functors;
functors.push_back(P(new DerivedFunctor1));
functors.push_back(P(new DerivedFunctor2));
for (auto &f : functors)
do_something(f1, *f);
}
A few other changes:
BaseFunctor
must have a virtual
destructor if objects of derived classes get deleted through BaseFunctor*
, and your code suggests that.override
instead of virtual
for the compiler to catch errors if you try to override a function that doesn't exists or have different parameters and/or return type. With virtual
it introduces a new function overload with the same name, in this case.std::unique_ptr
is used to avoid manual cleanup and leaking memory.BaseFunctor::desc
made const
, so that it must be initialized in the initializer list in BaseFunctor
. That also makes BaseFunctor
non-copyable and non-movable, which avoids accidental copy of derived class objects with slicing.