C++ 元编程:*必须*继承抽象的模板参数 class

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

我有一个摘要 class 可比较+可哈希值:

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

和一些具体的 class 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 class:

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

T 是存储在这些集合中的值的类型。 proto 应该是 T 的一个实例,它被用作类型 T 的 "null" 值,用于集合包含。我对 C++ 相当有经验,但对 TMP 不是特别有经验,虽然它看起来应该非常简单,但我似乎无法弄清楚我的伪代码 "class T inherits Key" 实际上是用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

Live Demo


另一种选择是使用 static_assert:

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

这可能更清楚一些,并为您提供更友好的错误消息,但您的类型约束不再在 class 声明中给出。


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

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
{};

Can somebody please tell me what the proper and idiomatic way to handle this situation in C++ would be?

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

下一个版本的 C++ 可能会支持明确包含此类注释,但在当前版本的 C++ 中,虽然您可以使用一些技巧,但在有限的情况下,惯用的方法是不要理会它。