C++: Inherit class from template parameter

后端 未结 4 1057
臣服心动
臣服心动 2021-01-30 14:18

I recently saw the following C++ code-snippet

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

and I am wondering in which setting such

相关标签:
4条回答
  • 2021-01-30 15:06

    It's often used to realize static polymorphism.

    Use cases are:

    • Policy-based design
    • Curiously recurring template pattern
    • Barton–Nackman trick

    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.

    0 讨论(0)
  • 2021-01-30 15:13

    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.

    0 讨论(0)
  • 2021-01-30 15:20

    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_;
    }
    
    0 讨论(0)
  • 2021-01-30 15:22

    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

    0 讨论(0)
提交回复
热议问题