Search code examples
c++c++14multiple-inheritancecrtp

multiple inheritance for function only - without virtual and CRTP


How to do multiple inheritance just for function?

  • must share data of the base class
  • no virtual function (assume that vtable is expensive)
  • avoid virtual inheritance
  • implementation must be able to reside in .cpp
  • c++14 is allowed

Here are similar questions :-

Here is a sample code (coliru demo) :-

class O{
    protected: int database=0;  
};
class A : public O{
    public: void print(){
        std::cout<<database<<std::endl;
    }
};
class B : public O{
    public: void set(int s){
        database=s+1;
    }
};
class AB : public O{
    public: void print(){//duplicate
        std::cout<<database<<std::endl;
    }
    public: void set(int s){//duplicate
        database=s+1;
    }
};
//AB ab;  ab.set(1); ab.print(); // would print 2

Here is my attempt (wandbox demo). I abuse CRTP :( :-

class O{
    public: int database=0; 
};
template<class T>class OA{
    public: void print(){
        std::cout<<static_cast<T*>(this)->database<<std::endl;
    }
};
template<class T>class OB{
    public: void set(int s){
        static_cast<T*>(this)->database=s+1;
    }
};
class A :public O,public OA<A>{};
class B :public O,public OB<B>{};
class AB :public O,public OA<AB>,public OB<AB>{};

It works, but it looks inelegant.
Furthermore, implementation must be in header (because OA and OB are template classes).

Are there better approaches? Or is this the way to go?

Sorry if it is too newbie question or already asked. I am a C++ beginner.

Edit

Give extended example of using please.

In ECS, it would be useful in some cases :-

class O{
    protected: EntityHandle e;  
};
class ViewAsPhysic : public O{                     //A
    public: void setTransform(Transformation t){
        Ptr<PhysicTransformComponent> g=e;
        g->transform=t;
    }
};
class ViewAsLight : public O{                      //B
    public: void setBrightness(int t){    
        Ptr<LightComponent> g=e;
        g->clan=t;
    }
};
class ViewAsLightBlock : public O{                 //AB
    //both functions 
};

Solution

  • The problem here is that the database field is member of class O. So without virtual inheritance, A and B will have each their own copy of database. So you must find a way to force A and B to share same value. You could for example use a reference field initialized in a protected constructor:

    #include <iostream>
    
    class O{
        int _db;
        protected: int &database;
        O(): database(_db) {};
        O(int &db): database(db) {};
    };
    class A : public O{
        public: void print(){
            std::cout<<database<<std::endl;
        }
        A() {}                                // public default ctor
        protected: A(int& db): O(db) {};      // protectect ctor
    };
    class B : public O{
        public: void set(int s){
            database=s+1;
        }
        B() {}                                // public default ctor
        protected: B(int& db): O(db) {};      // protectect ctor
    };
    class AB : public A, public B {
        int _db2;
        public: AB(): A(_db2), B(_db2) {};    // initialize both references to same private var
    };
    
    int main() {
        AB ab;
        ab.set(1);
        ab.print();
        return 0;
    }
    

    displays as expected:

    2
    

    Above code uses no virtual inheritance, no virtual function and no templates, so method can safely implemented in cpp files. The class AB actually uses methods from its both parents and has still a coherent view on its underlying data. In fact it simulates an explicit virtual inheritance by building the common data in the most derived class and injecting in through protected constructors in its parents.