Зазвичай це не є обґрунтованим для 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
та функцію a begin
і end
член, який повертається const_iterator
. Якщо ви обмежите це класами, які успадковують list
тоді користувач, який має власний тип, який не успадковує, list
але забезпечує ці членські функції, а typedefs не зможе використовувати вашу observable_list
.
Вирішення цього питання є двома, одне з них - нічого не обмежувати і покладатися на друкування качок. Велике заперечення цього рішення полягає в тому, що воно передбачає величезну кількість помилок, які можуть важко сприймати користувачів. Іншим рішенням є визначення ознак для обмеження типу, що надається для задоволення вимог інтерфейсу. Важливе значення для цього рішення полягає в тому, що це додаткове написання, яке можна сприймати як дратівливе. Однак позитивною стороною є те, що ви зможете написати власні повідомлення про помилки a la 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 та риси типу.