Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have an abstract class for comparable+hashable values:

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

and some concrete class C which inherits this.

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

and I would like to implement a templated HashSet class:

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

T is the type of values stored in these sets. proto should be an instance of T which is used as the "null" value of type T for the purposes of set inclusion. I am reasonably experienced with C++ but not especially with TMP and, although it seems like something which should be embarrassingly simple to pull off, I cannot seem to figure out how something like my pseudo-code "class T inherits Key" is actually done in C++. I want to be able to create a hash-set of instances of C like:

HashSet<C, C::null> myset;

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

like image 362
Thomas Avatar asked Oct 09 '15 08:10

Thomas


1 Answers

You can use std::enable_if_t and std::is_base_of for this:

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

Now HashSet instantiations are only valid if T inherits from Key.

std::enable_if_t is a C++14 feature. You can use typename std::enable_if<...>::type if you're stuck with C++11.

Live Demo


Another option would be to use static_assert:

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

This is maybe a bit clearer and gives you a more friendly error message, but your type constraint is no longer given in the class declaration.


With Concepts we'll get clarity, better error messages and keep our constraints in the declaration:

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
{};
like image 67
TartanLlama Avatar answered Sep 21 '22 14:09

TartanLlama