Chcę się tylko upewnić, że T jest faktycznie wystąpieniem podklasy lub samą klasą. Kod wewnątrz funkcji, którą podałem, jest prawie nieistotny.
phant0m
6
wręcz przeciwnie, jest to bardzo istotne. Określa, czy włożenie pracy w ten test jest dobrym pomysłem. W wielu (wszystkich?) Przypadkach nie ma absolutnie potrzeby samodzielnego wymuszania takich ograniczeń, ale raczej pozwól kompilatorowi zrobić to podczas tworzenia instancji. Na przykład dla zaakceptowanej odpowiedzi dobrze byłoby sprawdzić, czy Tpochodzi z Baseclass. W tej chwili to sprawdzenie jest niejawne i nie jest widoczne w celu rozpoznania przeciążenia. Ale jeśli nigdzie nie ma takiego domniemanego ograniczenia, wydaje się, że nie ma powodu do sztucznego ograniczenia.
Johannes Schaub - litb
1
Tak, zgadzam się. Jednak chciałem tylko wiedzieć, czy jest sposób, aby to osiągnąć, czy nie :) Ale oczywiście masz bardzo ważny punkt i dziękuję za wgląd.
phant0m
Odpowiedzi:
53
W takim przypadku możesz:
template <classT>voidfunction(){
Baseclass *object = new T();
}
Nie zostanie to skompilowane, jeśli T nie jest podklasą klasy podstawowej (lub T jest klasą podstawową).
a tak, to dobry pomysł. dzięki! Rozumiem, że nie ma sposobu, aby zdefiniować to w definicji szablonu?
phant0m
2
@ phant0m: Dobrze. Nie możesz jawnie ograniczać parametrów szablonu (z wyjątkiem używania pojęć, które były brane pod uwagę dla c ++ 0x, ale następnie zostały odrzucone). Wszystkie ograniczenia powstają niejawnie w wyniku operacji, które na nim wykonujesz (innymi słowy, jedynym ograniczeniem jest „Typ musi obsługiwać wszystkie operacje, które są na nim wykonywane”).
wrzesień
1
ah ic. Wielkie dzięki za wyjaśnienie!
phant0m
8
To wykonuje konstruktora T () i wymaga istnienia konstruktora T (). Zobacz moją odpowiedź na sposób, który pozwala uniknąć tych wymagań.
Douglas Leeder,
3
Ładnie i jasno, ale to jest problem, jeśli T to klasa „ciężka”.
Zapisz
84
Korzystając z kompilatora zgodnego z C ++ 11, możesz zrobić coś takiego:
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...
...
}
}
Przetestowałem to przy użyciu kompilatora gcc 4.8.1 w środowisku CYGWIN - więc powinno działać również w środowiskach * nix.
U mnie działa to też tak: template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
Matthias Dieter Wallnöfer
1
Myślę, że jest to najbardziej czytelna odpowiedź, która pozwala uniknąć dodatkowego kodu w czasie wykonywania.
Kyle,
50
Aby wykonać mniej bezużyteczny kod w czasie wykonywania, możesz zajrzeć na stronę:
http://www.stroustrup.com/bs_faq2.html#constraints,
która zawiera kilka klas, które wydajnie wykonują test czasu kompilacji i generują ładniejsze komunikaty o błędach.
Dla mnie to najlepsza i najciekawsza odpowiedź. Koniecznie zajrzyj do FAQ Stroustrupa, aby dowiedzieć się więcej o wszystkich rodzajach ograniczeń, które możesz narzucić w podobny sposób.
To świetna odpowiedź. Czy są jakieś dobre sposoby na uniknięcie ostrzeżeń unused variable 'p'i unused variable 'pb'?
Filip S.
@FilipS. dodaj (void)pb;po B* pb = p;.
bit2shift
11
Nie potrzebujesz pojęć, ale możesz użyć SFINAE:
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
}
Zauważ, że spowoduje to utworzenie instancji funkcji tylko wtedy, gdy warunek zostanie spełniony, ale nie zapewni rozsądnego błędu, jeśli warunek nie zostanie spełniony.
A co jeśli zawiniesz wszystkie funkcje w ten sposób? przy okazji, co to wraca?
the_drow,
enable_ifTrwa drugi typ parametru, który domyślnie void. Wyrażenie enable_if< true, int >::typereprezentuje typ int. Nie bardzo rozumiem, jakie jest twoje pierwsze pytanie, możesz użyć SFINAE do wszystkiego, co chcesz, ale nie do końca rozumiem, co zamierzasz z tym zrobić we wszystkich funkcjach.
David Rodríguez - dribeas
7
Od C ++ 11 nie potrzebujesz Boost ani static_assert. C ++ 11 wprowadza is_base_of i enable_if. C ++ 14 wprowadza wygodny typ enable_if_t, ale jeśli utkniesz z C ++ 11, możesz po prostu użyć enable_if::typezamiast tego.
#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
}
Alternatywa 2
Od C ++ 17 mamy is_base_of_v. Rozwiązanie można dalej przepisać na:
#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
}
Alternatywa 3
Możesz także ograniczyć cały szablon. Możesz użyć tej metody do definiowania całych klas. Zwróć uwagę, jak enable_if_tusunięto drugi parametr z (poprzednio był ustawiony na void). Jego domyślna wartość to faktycznie void, ale nie ma to znaczenia, ponieważ jej nie używamy.
#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
}
Z dokumentacji parametrów szablonu widzimy, że typename = enable_if_t...jest to parametr szablonu z pustą nazwą. Używamy go po prostu do upewnienia się, że istnieje definicja typu. W szczególności enable_if_tnie zostanie zdefiniowany, jeśli Basenie jest podstawą T.
Powyższa technika jest podana jako przykład w enable_if.
T
pochodzi zBaseclass
. W tej chwili to sprawdzenie jest niejawne i nie jest widoczne w celu rozpoznania przeciążenia. Ale jeśli nigdzie nie ma takiego domniemanego ograniczenia, wydaje się, że nie ma powodu do sztucznego ograniczenia.Odpowiedzi:
W takim przypadku możesz:
template <class T> void function(){ Baseclass *object = new T(); }
Nie zostanie to skompilowane, jeśli T nie jest podklasą klasy podstawowej (lub T jest klasą podstawową).
źródło
Korzystając z kompilatora zgodnego z C ++ 11, możesz zrobić coś takiego:
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... ... } }
Przetestowałem to przy użyciu kompilatora gcc 4.8.1 w środowisku CYGWIN - więc powinno działać również w środowiskach * nix.
źródło
template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");
...Aby wykonać mniej bezużyteczny kod w czasie wykonywania, możesz zajrzeć na stronę: http://www.stroustrup.com/bs_faq2.html#constraints, która zawiera kilka klas, które wydajnie wykonują test czasu kompilacji i generują ładniejsze komunikaty o błędach.
W szczególności:
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>(); }
źródło
unused variable 'p'
iunused variable 'pb'
?(void)pb;
poB* pb = p;
.Nie potrzebujesz pojęć, ale możesz użyć SFINAE:
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 }
Zauważ, że spowoduje to utworzenie instancji funkcji tylko wtedy, gdy warunek zostanie spełniony, ale nie zapewni rozsądnego błędu, jeśli warunek nie zostanie spełniony.
źródło
enable_if
Trwa drugi typ parametru, który domyślnievoid
. Wyrażenieenable_if< true, int >::type
reprezentuje typint
. Nie bardzo rozumiem, jakie jest twoje pierwsze pytanie, możesz użyć SFINAE do wszystkiego, co chcesz, ale nie do końca rozumiem, co zamierzasz z tym zrobić we wszystkich funkcjach.Od C ++ 11 nie potrzebujesz Boost ani
static_assert
. C ++ 11 wprowadzais_base_of
ienable_if
. C ++ 14 wprowadza wygodny typenable_if_t
, ale jeśli utkniesz z C ++ 11, możesz po prostu użyćenable_if::type
zamiast tego.Alternatywa 1
Rozwiązanie Davida Rodrígueza można przepisać w następujący sposób:
#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 }
Alternatywa 2
Od C ++ 17 mamy
is_base_of_v
. Rozwiązanie można dalej przepisać na:#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 }
Alternatywa 3
Możesz także ograniczyć cały szablon. Możesz użyć tej metody do definiowania całych klas. Zwróć uwagę, jak
enable_if_t
usunięto drugi parametr z (poprzednio był ustawiony na void). Jego domyślna wartość to faktycznievoid
, ale nie ma to znaczenia, ponieważ jej nie używamy.#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 }
Z dokumentacji parametrów szablonu widzimy, że
typename = enable_if_t...
jest to parametr szablonu z pustą nazwą. Używamy go po prostu do upewnienia się, że istnieje definicja typu. W szczególnościenable_if_t
nie zostanie zdefiniowany, jeśliBase
nie jest podstawąT
.Powyższa technika jest podana jako przykład w
enable_if
.źródło
template <class T : Base>
Można użyć Boost, Concept Sprawdź „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() { //... }
źródło
Wywołując funkcje wewnątrz szablonu, które istnieją w klasie bazowej.
Jeśli spróbujesz utworzyć wystąpienie szablonu z typem, który nie ma dostępu do tej funkcji, zostanie wyświetlony błąd kompilacji.
źródło
T
to, że tak jest,BaseClass
ponieważ zadeklarowani członkowieBaseClass
mogliby zostać powtórzeni w deklaracjiT
.