c++元编程:必须继承抽象类的模板参数

C++ meta-programming: A template parameter which *must* inherit an abstract class

本文关键字:抽象类 参数 继承 编程 c++      更新时间:2023-10-16

我有一个用于比较+哈希值的抽象类:

class Key
{
public:
  virtual bool operator ==(const Key&) const = 0;
  virtual bool operator !=(const Key&) const = 0;
  virtual u32 hashcode() const = 0;
};

和继承它的具体类C

class C : public Key
{
private:
  u32 a, b;
public:
  static const C& null; // a prototype for representing a "no value" C
  // Some reasonable implementation; it's just a pair
  // ...
};

和我想实现一个模板化HashSet类:

template<class T inherits Key, const T& proto> class HashSet
{
  //...
};

T是存储在这些集合中的值的类型。prototype应该是T的一个实例,用于集合包含的目的,作为类型T的"空"值。我对c++有相当的经验,但对TMP不是特别有经验,尽管它看起来应该是令人尴尬的简单实现,但我似乎无法弄清楚像我的伪代码"类T继承密钥"这样的东西实际上是如何在c++中完成的。我希望能够创建C实例的哈希集,如:

HashSet<C, C::null> myset;

谁能告诉我在c++中处理这种情况的合适和习惯的方法是什么?谢谢你!

您可以使用std::enable_if_tstd::is_base_of:

template<class T, const T& proto, 
         std::enable_if_t<std::is_base_of<Key,T>::value>* = nullptr> 
class HashSet
{
  //...
};

现在HashSet实例化只有在T继承Key时才有效。

std::enable_if_t是c++ 14的一个特性。如果你坚持使用c++ 11,你可以使用typename std::enable_if<...>::type

现场演示


另一个选项是使用static_assert:

template<class T, const T& proto>
class HashSet
{
    static_assert(std::is_base_of<Key, T>::value, "T must inherit from Key");
};

这可能更清楚一些,给你一个更友好的错误消息,但是你的类型约束不再在类声明中给出。


使用概念,我们将获得更清晰、更好的错误消息,并在声明中保留约束:

template <class Base, class Derived>                                                                                                                                                                                                           
concept bool IsBaseOf = std::is_base_of<Base, Derived>::value;
template<class T, const T& proto>
requires IsBaseOf<Key,T>
class HashSet
{};

谁能告诉我在c++中处理这种情况的合适和习惯的方法是什么?

那将简单地不处理它。如果用户传入一个从Key派生的类型,那么即使您没有在代码注释中将其作为显式要求添加,模板实例化也会工作。如果用户传入一个无效的模板参数,那么事情将会中断。

下一个版本的c++可能会支持清楚地包括这样的注释,但在当前的c++版本中,虽然有一些技巧可以使用,但除了在有限的情况下,惯用的方法是不要麻烦它。