Ich möchte nur sicherstellen, dass T tatsächlich eine Instanz einer Unterklasse oder der Klasse selbst ist. Der Code in der von mir bereitgestellten Funktion ist ziemlich irrelevant.
Phant0m
6
im Gegenteil, es ist sehr relevant. Es bestimmt, ob es eine gute Idee ist, Arbeit in diesen Test zu stecken oder nicht. In vielen (allen?) Fällen besteht absolut keine Notwendigkeit, solche Einschränkungen selbst durchzusetzen, sondern der Compiler dies beim Instanziieren tun zu lassen. Für die akzeptierte Antwort wäre es beispielsweise gut, zu überprüfen, ob von Tabgeleitet Baseclass. Ab sofort ist diese Überprüfung implizit und für die Überlastungsauflösung nicht sichtbar. Wenn jedoch nirgends eine solche implizite Einschränkung vorgenommen wird, scheint es keinen Grund für eine künstliche Einschränkung zu geben.
Johannes Schaub - litb
1
Ja, ich stimme zu. Ich wollte jedoch nur wissen, ob es einen Weg gibt, dies zu erreichen oder nicht :) Aber natürlich haben Sie einen sehr gültigen Punkt und bedanken sich für die Einsicht.
Phant0m
Antworten:
51
In diesem Fall können Sie Folgendes tun:
template <classT>voidfunction(){
Baseclass *object = new T();
}
Dies wird nicht kompilieren , wenn T nicht eine Unterklasse von Basisklasse (oder T ist Basisklasse).
Ach ja, das ist eine gute Idee. Vielen Dank! Ich nehme an, es gibt keine Möglichkeit, es in der Vorlagendefinition zu definieren.
Phant0m
2
@ phant0m: Richtig. Sie können Vorlagenparameter nicht explizit einschränken (außer bei der Verwendung von Konzepten, die für c ++ 0x berücksichtigt, dann aber gelöscht wurden). Alle Einschränkungen werden implizit durch die Operationen ausgeführt, die Sie daran ausführen (oder mit anderen Worten, die einzige Einschränkung ist "Der Typ muss alle Operationen unterstützen, die daran ausgeführt werden").
sepp2k
1
ah ic. Vielen Dank für die Klarstellung!
Phant0m
8
Dies führt den T () -Konstruktor aus und erfordert die Existenz des T () -Konstruktors. Siehe meine Antwort für einen Weg, der diese Anforderungen vermeidet.
Douglas Leeder
3
Schön und klar, aber das ist ein Problem, wenn T eine "schwere" Klasse ist.
3Dave
84
Mit einem C ++ 11-kompatiblen Compiler können Sie Folgendes tun:
template<classDerived>classMyClass {
MyClass() {
// Compile-time sanity checkstatic_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");
// Do other construction related stuff...
...
}
}
Ich habe dies mit dem Compiler gcc 4.8.1 in einer CYGWIN-Umgebung getestet - daher sollte es auch in * nix-Umgebungen funktionieren.
Bei mir funktioniert es auch so: template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
Matthias Dieter Wallnöfer
1
Ich denke, dies ist die am besten lesbare Antwort, die zusätzlichen Code zur Laufzeit vermeidet.
Kyle
50
Informationen zur Ausführung von weniger nutzlosem Code zur Laufzeit finden Sie unter:
http://www.stroustrup.com/bs_faq2.html#constraints
, das einige Klassen bereitstellt, die den Test zur Kompilierungszeit effizient durchführen und schönere Fehlermeldungen erzeugen.
Für mich ist dies die beste und interessanteste Antwort. Lesen Sie unbedingt die häufig gestellten Fragen zu Stroustrup, um mehr über alle Arten von Einschränkungen zu erfahren, die Sie ähnlich wie diese durchsetzen könnten.
Dies ist eine großartige Antwort. Gibt es gute Möglichkeiten, um die Warnungen zu vermeiden unused variable 'p'und unused variable 'pb'?
Filip S.
@FilipS. (void)pb;nachher hinzufügen B* pb = p;.
Bit2shift
11
Sie benötigen keine Konzepte, können aber SFINAE verwenden:
template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function(){
// This function will only be considered by the compiler if// T actualy derived from Base
}
Beachten Sie, dass dies die Funktion nur instanziiert, wenn die Bedingung erfüllt ist, aber keinen vernünftigen Fehler liefert, wenn die Bedingung nicht erfüllt ist.
Was ist, wenn Sie alle Funktionen so umschließen? Übrigens, was gibt es zurück?
the_drow
Der enable_ifnimmt einen zweiten Parameter, der standardmäßig verwendet wird void. Der Ausdruck enable_if< true, int >::typerepräsentiert den Typ int. Ich kann nicht wirklich verstehen, was Ihre erste Frage ist. Sie können SFINAE für alles verwenden, was Sie möchten, aber ich verstehe nicht ganz, was Sie damit über alle Funktionen hinweg vorhaben.
David Rodríguez - Dribeas
7
Seit C ++ 11 benötigen Sie Boost oder nicht mehr static_assert. C ++ 11 führt ein is_base_of und enable_if. In C ++ 14 wird der Convenience-Typ eingeführt enable_if_t. Wenn Sie jedoch mit C ++ 11 nicht weiterkommen, können Sie ihn enable_if::typestattdessen einfach verwenden .
Alternative 1
Die Lösung von David Rodríguez kann wie folgt umgeschrieben werden:
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Alternative 2
Seit C ++ 17 haben wir is_base_of_v. Die Lösung kann weiter umgeschrieben werden in:
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Alternative 3
Sie können auch einfach die gesamte Vorlage einschränken. Mit dieser Methode können Sie ganze Klassen definieren. Beachten Sie, wie der zweite Parameter von enable_if_tentfernt wurde (er wurde zuvor auf void gesetzt). Der Standardwert ist tatsächlich void, aber es spielt keine Rolle, da wir ihn nicht verwenden.
#include<type_traits>usingnamespacestd;
template <typename T,
typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Aus der Dokumentation der Vorlagenparameter geht hervor, dass typename = enable_if_t...es sich um einen Vorlagenparameter mit einem leeren Namen handelt. Wir verwenden es einfach, um sicherzustellen, dass die Definition eines Typs vorhanden ist. Insbesondere enable_if_twird nicht definiert, wenn Basekeine Basis von ist T.
Die obige Technik ist als Beispiel in angegeben enable_if.
Durch Aufrufen von Funktionen in Ihrer Vorlage, die in der Basisklasse vorhanden sind.
Wenn Sie versuchen, Ihre Vorlage mit einem Typ zu instanziieren, der keinen Zugriff auf diese Funktion hat, wird ein Fehler beim Kompilieren angezeigt.
Dies stellt nicht sicher, dass dies der Fall Tist,BaseClass da die deklarierten Mitglieder BaseClassin der Deklaration von wiederholt werden könnten T.
T
abgeleitetBaseclass
. Ab sofort ist diese Überprüfung implizit und für die Überlastungsauflösung nicht sichtbar. Wenn jedoch nirgends eine solche implizite Einschränkung vorgenommen wird, scheint es keinen Grund für eine künstliche Einschränkung zu geben.Antworten:
In diesem Fall können Sie Folgendes tun:
template <class T> void function(){ Baseclass *object = new T(); }
Dies wird nicht kompilieren , wenn T nicht eine Unterklasse von Basisklasse (oder T ist Basisklasse).
quelle
Mit einem C ++ 11-kompatiblen Compiler können Sie Folgendes tun:
template<class Derived> class MyClass { MyClass() { // Compile-time sanity check static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass"); // Do other construction related stuff... ... } }
Ich habe dies mit dem Compiler gcc 4.8.1 in einer CYGWIN-Umgebung getestet - daher sollte es auch in * nix-Umgebungen funktionieren.
quelle
template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");
...Informationen zur Ausführung von weniger nutzlosem Code zur Laufzeit finden Sie unter: http://www.stroustrup.com/bs_faq2.html#constraints , das einige Klassen bereitstellt, die den Test zur Kompilierungszeit effizient durchführen und schönere Fehlermeldungen erzeugen.
Speziell:
template<class T, class B> struct Derived_from { static void constraints(T* p) { B* pb = p; } Derived_from() { void(*p)(T*) = constraints; } }; template<class T> void function() { Derived_from<T,Baseclass>(); }
quelle
unused variable 'p'
undunused variable 'pb'
?(void)pb;
nachher hinzufügenB* pb = p;
.Sie benötigen keine Konzepte, können aber SFINAE verwenden:
template <typename T> boost::enable_if< boost::is_base_of<Base,T>::value >::type function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Beachten Sie, dass dies die Funktion nur instanziiert, wenn die Bedingung erfüllt ist, aber keinen vernünftigen Fehler liefert, wenn die Bedingung nicht erfüllt ist.
quelle
enable_if
nimmt einen zweiten Parameter, der standardmäßig verwendet wirdvoid
. Der Ausdruckenable_if< true, int >::type
repräsentiert den Typint
. Ich kann nicht wirklich verstehen, was Ihre erste Frage ist. Sie können SFINAE für alles verwenden, was Sie möchten, aber ich verstehe nicht ganz, was Sie damit über alle Funktionen hinweg vorhaben.Seit C ++ 11 benötigen Sie Boost oder nicht mehr
static_assert
. C ++ 11 führt einis_base_of
undenable_if
. In C ++ 14 wird der Convenience-Typ eingeführtenable_if_t
. Wenn Sie jedoch mit C ++ 11 nicht weiterkommen, können Sie ihnenable_if::type
stattdessen einfach verwenden .Alternative 1
Die Lösung von David Rodríguez kann wie folgt umgeschrieben werden:
#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of<Base, T>::value, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Alternative 2
Seit C ++ 17 haben wir
is_base_of_v
. Die Lösung kann weiter umgeschrieben werden in:#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of_v<Base, T>, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Alternative 3
Sie können auch einfach die gesamte Vorlage einschränken. Mit dieser Methode können Sie ganze Klassen definieren. Beachten Sie, wie der zweite Parameter von
enable_if_t
entfernt wurde (er wurde zuvor auf void gesetzt). Der Standardwert ist tatsächlichvoid
, aber es spielt keine Rolle, da wir ihn nicht verwenden.#include <type_traits> using namespace std; template <typename T, typename = enable_if_t<is_base_of_v<Base, T>>> void function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Aus der Dokumentation der Vorlagenparameter geht hervor, dass
typename = enable_if_t...
es sich um einen Vorlagenparameter mit einem leeren Namen handelt. Wir verwenden es einfach, um sicherzustellen, dass die Definition eines Typs vorhanden ist. Insbesondereenable_if_t
wird nicht definiert, wennBase
keine Basis von istT
.Die obige Technik ist als Beispiel in angegeben
enable_if
.quelle
template <class T : Base>
Sie könnten verwenden Boost - Concept Check ‚s
BOOST_CONCEPT_REQUIRES
:#include <boost/concept_check.hpp> #include <boost/concept/requires.hpp> template <class T> BOOST_CONCEPT_REQUIRES( ((boost::Convertible<T, BaseClass>)), (void)) function() { //... }
quelle
Durch Aufrufen von Funktionen in Ihrer Vorlage, die in der Basisklasse vorhanden sind.
Wenn Sie versuchen, Ihre Vorlage mit einem Typ zu instanziieren, der keinen Zugriff auf diese Funktion hat, wird ein Fehler beim Kompilieren angezeigt.
quelle
T
ist,BaseClass
da die deklarierten MitgliederBaseClass
in der Deklaration von wiederholt werden könntenT
.