user695652
user695652

Reputation: 4275

C++: Inherit class from template parameter

I recently saw the following C++ code-snippet

template <class B>
class A : public B
{
   ...
};

and I am wondering in which setting such a design is good practice?

The way I understand it, is that having the superclass as a template parameter allows users of A to choose a superclass when instantiating an object of A.

But if this is the case, wouldn't it be better to have a common superclass C for all the classes (B) which are used as the template argument and have A extend C ?

Upvotes: 31

Views: 15548

Answers (4)

uchar
uchar

Reputation: 2590

A place where I use this style was where I need to implement a generic graph library which is both easy to use and also easy to maintain After a while I came with this design :

ABstract class for GraphContainer,Edge,Node :

template < class T1,class T2>
class  GraphAbstractContainer
{
public:
    using Node = T1;
    using Edge = T2;
    virtual std::list<Node> getConnectedNodes(const Node& node)const = 0;
    virtual Node addNode(const Node&) = 0;
    //...
};

class  GraphAbstracthNode
{
public:
    virtual uint32_t getId() const = 0;
    virtual void setID(uint32_t id)=0;
    //..
};

template<class T>
class  GraphAbstractEdge
{
public:
    using Node = T;
    //GraphAbstractEdge(){}
    virtual Node  firstNode() const = 0;
    virtual Node   secondNode() const = 0;
    virtual void  setFirstNode(const Node& node)  = 0;
    virtual void  setSecondNode(const Node& node) = 0;
    //...

};

Then I add Adj_List and Adj Matrix implementation by inheriting directly from template parametrs .

for example My Adj List classess looks some thing like this :

template<class T1 = GraphAbstractContainer<GraphAdjNode,
                   GraphAdjEdge>>
class  GraphAdjListContainer : public T1
{
public:
    using Node = typename T1::Node;
    using Edge = typename T1::Edge;

    //return connected Nodes
    virtual std::list<Node> getConnectedNodes(const Node& node) const
    {
        //..
    }
    //..
  };

};

template<class T>
class  GraphAdjNode : public T
{
public:
    //implementing abstract class methods...
};

template<class T>
class  GraphAdjEdge : public T
{
public:
   //...

};

And also My Graph class inherit directly from template too :

template<class GraphContainer=GraphAdjListContainer<>>
    class   Graph :public  GraphContainer
    {
    public:
        using Node = typename GraphContainer::Node;
        using Edge = typename GraphContainer::Edge;
         //...

}

An advantage for this design pattern is you can simply change the whole class underlying's stuffs by just inherit from abstract classes and fill the template parametrs.

for example I define Trie data structure by simply doing this :

class TrieNode :public GraphAdjNode
{
public:
    //...
    std::string word_;
};

class Trie 
{
public:
    using Graph = Graph < ecv::GraphAdjListContainer<TrieNode, ecv::GraphAdjListEdge<TrieNode>>>;
    using Node =  Graph::Node;
    using Edge =  Graph::Edge;
    void addWord(wstring word);
    //...
private:
    Graph graph_;
}

Upvotes: 1

vz0
vz0

Reputation: 32923

Sounds like a good candidate for a wrapper class:

class base {
public:
  virtual void f() = 0;
};

class d1 : public base {
public:
  virtual void f() override { ... };
};

class d2 : public base {
public:
  virtual void f() override { ... };
};

template <typename T>
class wrapper : public T {
public:
  virtual void f() override {
    pre_op();
    T::f();
    post_op();
  }

private:
  void pre_op() { ... }
  void post_op() { ... }
}

int main(int, char**) {
  wrapper<d1> w1;
}

For example, the wrapper class can provide synchronized access to the derived classes.

Upvotes: 6

hansmaad
hansmaad

Reputation: 18905

It's often used to realize static polymorphism.

Use cases are:

In general you have the benefits from dynamic polymorphism, without the extra runtime costs of virtual functions. But it's only useful if the concrete type can be determined at compile time.

Upvotes: 19

vsoftco
vsoftco

Reputation: 56567

It is used frequently in the so called "policy-based" design, i.e. you add characteristics to a base class by composition with desired derived classes, see "Modern C++ Design: Generic Programming and Design Patterns Applied" by Andrei Alexandrescu. The instantiated template class from which you derive is called the "policy". Such a design is sometimes better than inheritance, as it allows to combine policies and avoid a combinatorial explosion inevitable in the inheritance-based model.

See for example the following simple code, where RED and BLUE are drawing policies for a Pen:

#include <iostream>
#include <string>

struct RED
{
    std::string getColor()
    {
        return "RED";
    }
};

struct BLUE
{
    std::string getColor()
    {
        return "BLUE";
    }
};

template <typename PolicyClass>
class Pencil: public PolicyClass
{
public:
    void Draw()
    {
        std::cout << "I draw with the color " << PolicyClass::getColor() << std::endl; 
    }
};


int main()
{   
    Pencil<RED> red_pencil; // Drawing with RED
    red_pencil.Draw();
    Pencil<BLUE> blue_pencil; // Different behaviour now
    blue_pencil.Draw();

    return 0;
}

Can read a bit more here: http://en.wikipedia.org/wiki/Policy-based_design

Upvotes: 2

Related Questions