C++;:从模板参数继承类 我最近看到了下面的C++代码片段< /p> template <class B> class A : public B { ... }; 模板 A类:公共B类 { ... };

C++;:从模板参数继承类 我最近看到了下面的C++代码片段< /p> template <class B> class A : public B { ... }; 模板 A类:公共B类 { ... };,c++,templates,inheritance,design-patterns,C++,Templates,Inheritance,Design Patterns,我想知道在哪种环境下这样的设计是好的做法 我的理解是,将超类作为模板参数允许a的用户在实例化a的对象时选择超类 但是如果是这种情况,对所有用作模板参数且具有a扩展C的类(B)使用一个公共超类C不是更好吗?听起来像是包装类的一个很好的候选者: class base { public: virtual void f() = 0; }; class d1 : public base { public: virtual void f() override { ... }; }; class

我想知道在哪种环境下这样的设计是好的做法

我的理解是,将超类作为模板参数允许a的用户在实例化a的对象时选择超类


但是如果是这种情况,对所有用作模板参数且具有a扩展C的类(B)使用一个公共超类C不是更好吗?

听起来像是包装类的一个很好的候选者:

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;
}
类基{
公众:
虚空f()=0;
};
d1类:公共基地{
公众:
虚空f()覆盖{…};
};
d2类:公共基地{
公众:
虚空f()覆盖{…};
};
模板
类包装器:public T{
公众:
虚拟void f()覆盖{
pre_op();
T::f();
post_op();
}
私人:
void pre_op(){…}
void post_op(){…}
}
int main(int,char**){
包装物w1;
}

例如,包装类可以提供对派生类的同步访问。

它经常被用于所谓的“基于策略”的设计,即通过将所需的派生类组合起来,将特性添加到基类中,参见Andrei Alexandrescu的“现代C++设计:泛型编程和设计模式”。从中派生的实例化模板类称为“策略”。这种设计有时比继承更好,因为它允许组合策略,避免基于继承的模型中不可避免的组合爆炸

例如,请参见以下简单代码,其中
红色
蓝色
绘制策略:

#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;
}
#包括
#包括
结构红
{
std::string getColor()
{
返回“红色”;
}
};
结构蓝
{
std::string getColor()
{
返回“蓝色”;
}
};
模板
课堂:公共政策课
{
公众:
作废提款()
{

std::cout通常用于实现

用例包括:


一般来说,动态多态性可以带来好处,而不需要虚拟函数额外的运行时成本。但只有在编译时确定具体类型时,它才有用。

我使用这种样式的地方是,我需要实现一个通用图库,它既易于使用,也易于维护 过了一段时间,我想到了这个设计:

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;
    //...

};
模板
类GraphAbstractContainer
{
公众:
使用Node=T1;
使用Edge=T2;
虚拟std::list getConnectedNodes(const节点和节点)const=0;
虚拟节点addNode(const Node&)=0;
//...
};
类GraphAbstracthNode
{
公众:
虚拟uint32_t getId()常量=0;
虚拟void setID(uint32_t id)=0;
//..
};
模板
类GraphAbstractEdge
{
公众:
使用Node=T;
//GraphAbstractEdge(){}
虚拟节点firstNode()常量=0;
虚拟节点secondNode()常量=0;
虚空setFirstNode(常量节点和节点)=0;
虚拟void setSecondNode(常量节点和节点)=0;
//...
};
然后我通过直接从模板参数继承添加Adj_列表和Adj矩阵实现

例如,My Adj List classess看起来像这样:

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:
   //...

};
模板
类GraphAdjListContainer:公共T1
{
公众:
使用Node=typename T1::Node;
使用Edge=typename T1::Edge;
//返回连接的节点
虚拟标准::列出getConnectedNodes(常量节点和节点)常量
{
//..
}
//..
};
};
模板
类GraphAdjNode:public T
{
公众:
//正在实现抽象类方法。。。
};
模板
类graphhadjedge:public T
{
公众:
//...
};
我的Graph类也直接从模板继承:

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

}
模板
类图形:公共图形容器
{
公众:
使用Node=typename GraphContainer::Node;
使用Edge=typename GraphContainer::Edge;
//...
}
这种设计模式的一个优点是,只需从抽象类继承并填充模板参数,就可以简单地更改整个类的底层内容

例如,我通过简单地执行以下操作来定义Trie数据结构:

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_;
}
class三节点:公共图形节点
{
公众:
//...
std::字符串字;
};
三类
{
公众:
使用Graph=Graph;
使用Node=Graph::Node;
使用Edge=Graph::Edge;
无效添加字(wstring字);
//...
私人:
图;
}

通常情况下是相反的。但您显示的模式可用于策略或接口注入。@πάνταῥεῖ: 设计模式与具体的用例无关。@user695652不应该这样,如果它真的关闭了,我会重新投票给你。@πάνταῥεῖ: 设计模式没有动机部分。关于它们的书籍或文章有动机部分,但没有动机部分。你是在暗示每一个缺乏示例的抽象问题都过于宽泛吗?事实上,我有时会收到错误的问题答案,因为我添加了示例;也就是说,我得到的答案是针对我的示例而不是问题的,所以跳过了一次考试le有时实际上更好。好的,我收回了我的投票。我渴望阅读复杂的答案;)继承和拥有相同类型的成员似乎是一种浪费,为什么不调用父成员函数
T::f()
?这一点很好,但您的示例并不能证明继承是正确的。它可能是组合的。@Ernon这是一个简单的示例,但在更现实的场景中,您希望编写基类以便它与任何策略一起工作,这就是为什么(据我所知),您从临时