Search code examples
c++templatescontainersderived-classbase-class

DElem<T,N> derives from BElem<T> and DContainer<DElem<T,N>> derives from BContainer<BElem<T>> How to code it?


The question is easy to explain in code.

I have coded several template classes that they derive from a unique template class:

template<typename T,unsigned N> 
struct DElem : 
    public BElem<T> 
{};

My problem arises when I have to code a container of these anterior derived types from a container of the base class:

template<typename T, unsigned N>
struct DContainer<DElem<T,N>> : 
    public BContainer<BElem<T>>
{};

In my concrete case, Container could be std::tuple or std::array.

My first approximation is:

template<typename T, T B, std::size_t N>
struct DContainer : 
    public std::array<BElem<T>,N> 
{

   // This container is to hold **DElem<T,B>**
   // 
   // This class has to do a cast for every
   // 
   // **DElem<T,B>** (that is what the DContainer holds) 
   //
   // to **BElem\<T\>**
   // 
   // *If this task is easy I don't found the way*

};

Someone has an idea to do these tasks more easy or some other design more appropiate?


Solution

  • You are out of luck. A container of DElem<T, N> is not substitutable for a container of BElem<T>. If it could, the following nonsense would be allowed.

    DContainer<T, 10> d10Container;
    BContainer<T> & bContainer = d10Container;
    DElem<T, 20> d20;
    bContainer.push_back(d20); // pushed a d20 into a container of d10
    

    What you can have is a view of BElem<T>

    template<typename T>
    class BView {
        class iterator {
            using value_type = BElem<T>;
            using reference = BElem<T> &;
            using pointer = BElem<T> *;
            using difference_type = std::ptrdiff_t;
            using iterator_category = std::forward_iterator_tag; // or whatever
            reference operator*();
            pointer operator->();
            // etc...
        };
    
        virtual iterator begin() = 0;
        virtual iterator end() = 0;
    
        // no insert / push_back / whatever
    };
    

    You probably want to hide (or delete) the assignment operator of BElem<T>, because polymorphic assignment is also fairly nonsensical.

    Now you have a shared base class for all your DContainer<T, N>s which doesn't permit nonsense.

    Alternatively, if you don't need runtime polymorphism, you can just define a concept for BContainer. Using the pre-concept Container requirements as a base:

    template<container C, typename T>
    concept BContainer = std::derived_from<typename C::value_type, BElem<T>>;