Jak mogę używać CRTP w C ++, aby uniknąć narzutu wirtualnych funkcji członkowskich?
89
Istnieją dwa sposoby.
Pierwszym jest statyczne określenie interfejsu dla struktury typów:
template <class Derived>
struct base {
void foo() {
static_cast<Derived *>(this)->foo();
};
};
struct my_type : base<my_type> {
void foo(); // required to compile.
};
struct your_type : base<your_type> {
void foo(); // required to compile.
};
Drugi polega na unikaniu stosowania idiomu odniesienia do bazy lub wskaźnika do podstawy i wykonywaniu okablowania w czasie kompilacji. Korzystając z powyższej definicji, możesz mieć funkcje szablonów, które wyglądają następująco:
template <class T> // T is deduced at compile-time
void bar(base<T> & obj) {
obj.foo(); // will do static dispatch
}
struct not_derived_from_base { }; // notice, not derived from base
// ...
my_type my_instance;
your_type your_instance;
not_derived_from_base invalid_instance;
bar(my_instance); // will call my_instance.foo()
bar(your_instance); // will call your_instance.foo()
bar(invalid_instance); // compile error, cannot deduce correct overload
Zatem połączenie definicji struktury / interfejsu i odliczenia typu czasu kompilacji w twoich funkcjach pozwala na wysyłanie statyczne zamiast dynamicznego. Na tym polega istota statycznego polimorfizmu.
not_derived_from_base
nie pochodzi zbase
, ani nie pochodzi zbase
...Sam szukałem porządnych dyskusji na temat CRTP. Techniki Todda Veldhuizena dotyczące naukowego języka C ++ to świetne źródło informacji o tym (1.3) i wielu innych zaawansowanych technikach, takich jak szablony wyrażeń.
Odkryłem również, że większość oryginalnego artykułu Coplien o C ++ Gems można przeczytać w książkach Google. Może nadal tak jest.
źródło
dynamic_cast
lub metod wirtualnych.Musiałem sprawdzić CRTP . Jednak gdy to zrobiłem, znalazłem trochę rzeczy na temat statycznego polimorfizmu . Podejrzewam, że to jest odpowiedź na Twoje pytanie.
Okazuje się, że ATL dość szeroko wykorzystuje ten wzorzec.
źródło
Ta odpowiedź z Wikipedii zawiera wszystko, czego potrzebujesz. Mianowicie:
template <class Derived> struct Base { void interface() { // ... static_cast<Derived*>(this)->implementation(); // ... } static void static_func() { // ... Derived::static_sub_func(); // ... } }; struct Derived : Base<Derived> { void implementation(); static void static_sub_func(); };
Chociaż nie wiem, ile to faktycznie kosztuje. Narzut wywołania funkcji wirtualnej jest (oczywiście zależny od kompilatora):
Podczas gdy narzut statyczny polimorfizmu CRTP wynosi:
źródło