Мета-программирование на С++: параметр шаблона, который * должен * наследовать абстрактный класс
У меня есть абстрактный класс для сопоставимых + хешируемых значений:
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 just a pair
// ...
};
и я хотел бы реализовать шаблонный класс HashSet:
template<class T inherits Key, const T& proto> class HashSet
{
//...
};
T - тип значений, хранящихся в этих наборах. proto должен быть экземпляром T, который используется как "нулевое" значение типа T для целей включения. Я достаточно опытен с С++, но не особенно с TMP и, хотя кажется, что-то, что должно быть смущающе просто для снятия, я не могу понять, как что-то вроде моего псевдокода "класс T наследует Key" выполняется на С++. Я хочу иметь возможность создавать хеш-набор экземпляров C как:
HashSet<C, C::null> myset;
Может кто-нибудь, пожалуйста, скажите мне, какой правильный и идиоматический способ справиться с этой ситуацией на С++? Спасибо!
Ответы
Ответ 1
Вы можете использовать std::enable_if_t
и std::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
- это функция С++ 14. Вы можете использовать typename std::enable_if<...>::type
, если вы застряли с С++ 11.
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");
};
Это, возможно, немного яснее и дает вам более дружественное сообщение об ошибке, но ограничение вашего типа больше не указано в объявлении класса.
С Концепции мы получим ясность, лучшие сообщения об ошибках и сохраним наши ограничения в объявлении:
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
{};
Ответ 2
Может кто-нибудь, пожалуйста, скажите мне, какой правильный и идиоматический способ справиться с этой ситуацией на С++ будет?
Это просто не будет обрабатывать. Если пользователь передает тип, полученный из Key
, тогда создание экземпляра будет работать, даже если вы не добавите это как явное требование в аннотации кода. Если пользователь передает недопустимый аргумент шаблона, ожидается, что все будет разорвано.
В следующей версии С++, вероятно, будет поддержка четких включений таких аннотаций, но в текущей версии С++, хотя есть некоторые трюки, которые вы можете использовать, за исключением ограниченных обстоятельств, идиоматический способ - просто не беспокоиться об этом.