Как правило, это неоправданно в 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
and, 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 и черты типа.