Dies ist in C ++ normalerweise nicht gerechtfertigt, wie andere Antworten hier festgestellt haben. In C ++ neigen wir dazu, generische Typen basierend auf anderen Einschränkungen als "erbt von dieser Klasse" zu definieren. Wenn Sie das wirklich wollten, ist es in C ++ 11 ganz einfach und <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..
};
Dies bricht jedoch viele der Konzepte, die Menschen in C ++ erwarten. Es ist besser, Tricks wie das Definieren eigener Merkmale anzuwenden. observable_list
Möchte beispielsweise einen beliebigen Containertyp akzeptieren, der die typedefs const_iterator
und eine begin
und end
member-Funktion hat, die zurückgibt const_iterator
. Wenn Sie dies auf Klassen beschränken, die von list
einem Benutzer erben , der einen eigenen Typ hat, der nicht erbt, list
aber diese Mitgliedsfunktionen und typedefs bereitstellt, kann er Ihren nicht verwendenobservable_list
.
Es gibt zwei Lösungen für dieses Problem. Eine davon besteht darin, nichts einzuschränken und sich auf das Tippen von Enten zu verlassen. Ein großer Nachteil dieser Lösung ist, dass sie eine große Menge an Fehlern enthält, die für Benutzer schwer zu verstehen sind. Eine andere Lösung besteht darin, Merkmale zu definieren, um den bereitgestellten Typ zu beschränken, um die Schnittstellenanforderungen zu erfüllen. Der große Nachteil dieser Lösung besteht darin, dass zusätzliches Schreiben erforderlich ist, was als ärgerlich angesehen werden kann. Die positive Seite ist jedoch, dass Sie Ihre eigenen Fehlermeldungen a la schreiben können static_assert
.
Der Vollständigkeit halber wird die Lösung für das obige Beispiel angegeben:
#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...
};
Im obigen Beispiel werden viele Konzepte gezeigt, die die Funktionen von C ++ 11 veranschaulichen. Einige Suchbegriffe für Neugierige sind verschiedene Vorlagen, SFINAE, Ausdruck SFINAE und Typmerkmale.