Jak mogę używać CRTP w C ++, aby uniknąć narzutu wirtualnych funkcji członkowskich?
Jak mogę używać CRTP w C ++, aby uniknąć narzutu wirtualnych funkcji członkowskich?
Odpowiedzi:
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 z base
, ani nie pochodzi z base
...
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.
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.
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: