Как правило, это неоправданно в C ++, как отмечалось в других ответах. В C ++ мы стремимся определять универсальные типы на основе других ограничений, кроме «наследуется от этого класса». Если вы действительно хотели это сделать, это довольно легко сделать в C ++ 11 и <type_traits>
:
#include <type_traits>
template<typename T>
class observable_list {
static_assert(std::is_base_of<list, T>::value, "T must inherit from list");
// code here..
};
Это нарушает многие концепции, которые люди ожидают в C ++. Лучше использовать такие приемы, как определение собственных черт. Например, возможно, observable_list
хочет принять любой тип контейнера, который имеет функции typedefs const_iterator
и begin
и end
, которые возвращают const_iterator
. Если вы ограничите это классами, которые наследуют от list
, то пользователь, который имеет свой собственный тип, который не наследует от list
, но предоставляет эти функции-члены и typedefs, не сможет использовать ваш observable_list
.
Существует два решения этой проблемы, одно из которых - не ограничивать что-либо и полагаться на типизацию утки. Большим недостатком этого решения является то, что оно включает в себя огромное количество ошибок, которые могут быть сложными для пользователей. Другое решение состоит в том, чтобы определить признаки, чтобы ограничить тип, обеспеченный, чтобы удовлетворить требованиям интерфейса. Большой минус для этого решения заключается в том, что это требует дополнительной записи, что может показаться раздражающим. Тем не менее, положительным моментом является то, что вы сможете писать свои собственные сообщения об ошибках а-ля static_assert
.
Для полноты приведено решение для приведенного выше примера:
#include <type_traits>
template<typename...>
struct void_ {
using type = void;
};
template<typename... Args>
using Void = typename void_<Args...>::type;
template<typename T, typename = void>
struct has_const_iterator : std::false_type {};
template<typename T>
struct has_const_iterator<T, Void<typename T::const_iterator>> : std::true_type {};
struct has_begin_end_impl {
template<typename T, typename Begin = decltype(std::declval<const T&>().begin()),
typename End = decltype(std::declval<const T&>().end())>
static std::true_type test(int);
template<typename...>
static std::false_type test(...);
};
template<typename T>
struct has_begin_end : decltype(has_begin_end_impl::test<T>(0)) {};
template<typename T>
class observable_list {
static_assert(has_const_iterator<T>::value, "Must have a const_iterator typedef");
static_assert(has_begin_end<T>::value, "Must have begin and end member functions");
// code here...
};
В приведенном выше примере показано много концепций, демонстрирующих возможности C ++ 11. Некоторыми поисковыми терминами для любопытных являются шаблоны с переменными значениями, SFINAE, выражение SFINAE и черты типа.