如何改进自引用模板的实现

How to improve the self-referencing template implementation?

本文关键字:实现 何改进 自引用      更新时间:2023-10-16

如何在给定的自引用模板实现中去掉抽象类?

我只是尝试实现一个跳过列表数据结构。所以我想创建模板Node,这样我就可以为不同的节点类实例化下一个链接的类,以避免类强制转换。发现了这些问题:

模板参数中的自引用模板

如何正确声明自引用模板类型?

但他们都没有解决方案。然后,我基于两条继承线制定了自己的解决方案。一个是"抽象"模板的序列(用于下一个参数的传播)。另一个是实例化具体的类。但感觉在没有多余抽象模板(NodeAbstract、NodeWithKeyAbstract等)的情况下处理同样的问题是可以改进的。经过几次自己的尝试,我想请你帮助我:

template <class Value, class Next >
class NodeAbstract
{
public:
    Value m_value;
    Next * next;
    NodeAbstract () : next(0) {}
    Next * getNext() {return next;}
};
template <class Value, class Key, class Next >
class NodeWithKeyAbstract : public NodeAbstract <Value, Next >
{
public:
    Key m_key;
};
template <class Value, class Key>
class NodeWithKey : public NodeWithKeyAbstract <Value, Key, NodeWithKey<Value,Key> >
{
};
template <class Value, class Key, int maxlevel, class Next>
class NodeSkipListAbstract : public NodeWithKeyAbstract<Value, Key, Next >
{
public:
    Next * nextjump[maxlevel-1];
};
template <class Value, class Key, int maxlevel>
class NodeSkipList : public NodeSkipListAbstract<Value, Key, maxlevel, NodeSkipList<Value, Key, maxlevel> >
{
};

如果我理解正确的话,你的问题基本上是中不同的maxlevel值会产生不同的类,所以你不能使用一个数组来存储所有的类(如果我错了,请纠正我)。

你不能完全摆脱抽象类——如果你想把具有不同最大级别的节点作为不同的类(不同的模板专业化),你必须为它们提供一些共同点。

好消息是,你可以去掉Curioly Recurring Template Pattern——因为你使用指针,所以如果你是抽象的,你不必引用确切的实现类型(例如,知道确切的模板专业化),你可以访问你需要的所有信息。您的代码也可以简化一点。

考虑这个代码:

template <class Key, class Value>
class Node {
 public:
  virtual ~Node() = default;
  virtual std::size_t MaxLevel() const = 0;
  virtual Node* Skip(size_t level) const = 0;
  // add setter as well
  Key key;
  Value value;
};
template <class Key, class Value, std::size_t max_level>
class NodeImpl : public Node<Key, Value> {
 public:
  typedef Node<Key, Value> node_type;
  NodeImpl() : skips() {}
  size_t MaxLevel() const { return max_level; }
  node_type* Skip(std::size_t level) const {
    return level < max_level ? skips[level] : nullptr;
  }
  // add setter as well
 private:
  node_type* skips[max_level];
};
template <class Key, class Value>
class SkipList {
  public:
   typedef Node<Key, Value> node_type;
   node_type* head;
};

这里Node为您提供了一个"跳过"行为的抽象。NodeImpl将用于生成具有不同最大级别的Node,但最终使用的实现对您来说是透明的——您将只使用Node的接口。此外,在语法级别上,您将只使用Node*类型,所以各种实现都不会成为问题。虚拟析构函数将确保delete释放所有内存,并且keyvalue始终可以作为公共字段访问。

这个代码当然可以改进。原始数组可以用std::array替换。如果您决定使用在构造函数中设置大小的std::vector而不是数组(那么您将只有NodeSkipList),那么max_level作为模板的整个想法就可以摆脱了。作为奖励,创建新节点会更容易,因为现在你必须编写一些工厂,专门化所有NodeImpl的值,从1到某个值。另外,指针可以用一些智能指针代替,以避免内存泄漏。