Search code examples
c++expressionstdvector

C++ Store an expression template inside a class whose objects will be part of a vector


[Extensively edited for clarity and typos, and now with attempted solution, old version at the end]

New version of the question

I have a class which needs to store a functor. The problem is that the functor is an expression template (meaning that each functor is of different type). On top of that I need to collect all the objects of that class in a vector.

Below my attempts:

I do not write the code for the functors as expression templates as it is too long. I will therefore test the code by trying to store two functors of two different classes

struct FunctorTypeA {
    double a;
    FunctorTypeA(const double ap): a(ap) {}
    double operator()(const double x){ return a;}
};

struct FunctorTypeB {
    double a, b;
    FunctorTypeB(const double ap, const double bp): a(ap), b(bp) {}
    double operator()(const double x){ return a+b*x;}
};

I think, correct me if I am wrong, that if the code below works for the two functors above, it should work for any functor constructed with expression templates.

The first idea would be

template <typename functorType> struct myClass {
    functorType    functor;
    myClass(functorType funct): functor(funct){}
};

I can store a functor inside an object of that class with:

FunctorTypeA functor1(1.2);
FunctorTypeB functor2(1.5, 5.0);
myClass      myObj1(functor1);
myClass      myObj2(functor2);
cout << myObj1.functor(0.2) << " " << myObj1.functor(0.2) << "\n\n";

However I cannot store those objects in an std::vector as they have different types.

Therefore I tried:

struct myClass2Base {
    virtual ~TrialClassBase() = default;
    virtual double functor(const double x) = 0;
};

template <typename functorType> struct myClass2 : public myClass2Base {
    functorType    functorStored;
    myClass2(functorType funct): functorStored(funct){}
    double functor(const double x){return functorStored(x);}
};

and I can construct a vector of those objects as

std::vector<myClass2Base*> vecOfObj;
vecOfObj.push_back(new myClass2(functor1));
    
cout << vecOfObj[0]->functor(0.3) << "\n\n";

This one works. Is there a better solution?

Thanks!

Old version of the question

I have a class which needs to store a functor which is an expression template (meaning that each function is of different type). I also need to put the objects of that class in an std::vector.

My Attempt is

struct TrialClassBase{
    virtual double get_functor(double) = 0;
};

template <typename derived> struct TrialClass : public TrialClassBase {
    derived functor;
    TrialClass(derived fun): functor(fun){}
    double get_functor(double x) {return functor(x);}
};

std::vector<shared_ptr<TrialClassBase>> vecOfObjs;

Then I try to add an object to the vector. As an example I use an std::function, only as an example: the functor in my case will be an expression template.

std::function<double(double)>  funcccc = [](double x){return x*x;};
vecc.emplace_back(TrialClass(funcccc));
cout << vecc[0]->get_functor(0.3) <<"\n\n";

This fails to compile. What mistake am I making? How can I do what I want to do?

Thanks!


Solution

  • I see you have improved your question. Yes there are debatably better solutions.

    One nice solution the std lib offers you, is to use type-erasure using std:function.

    #include <vector>
    #include <functional>
    #include <iostream>
    #include <cmath>
    
    struct FunctorTypeA {
        double a;
        FunctorTypeA(const double ap): a(ap) {}
        auto operator()([[maybe_unused]]const double x) const { return a;}
    };
    
    
    int main(){
        auto funcVec{std::vector<std::function<double(double)>>{}};
        funcVec.push_back(FunctorTypeA{3.14}); // functor allowed
        funcVec.push_back([](double x){return x;}); // lambda allowed
        funcVec.push_back(sqrt); // function pointer allowed, e.g. C math function
    
        for(auto&& f:funcVec){
            std::cout << f(2.0) << '\n';
        }
    }
    

    This way you don't complicate things with inheritance and pointer casting. Easy to make mistakes there. std::vector and std::function will do all the cleaning up. (That was something you can easily miss in your vector-of-pointers solution)

    Note: std::function may perform a dynamic allocation. But so does std::vector and inheritance has vtable overhead.