Как я могу использовать CRTP в С++, чтобы избежать накладных расходов на действия виртуальных членов?
CRTP, чтобы избежать динамического полиморфизма
Ответ 1
Есть два способа.
Первый из них - это указание интерфейса статически для структуры типов:
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.
};
Во-вторых, избегайте использования ссылки на базовую или указатель-на-базу и выполняйте проводку во время компиляции. Используя приведенное выше определение, вы можете иметь функции шаблонов, которые выглядят так:
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
Таким образом, объединение определения структуры/интерфейса и вычета типа времени компиляции в ваших функциях позволяет выполнять статическую диспетчеризацию вместо динамической отправки. В этом суть статического полиморфизма.
Ответ 2
Я искал достойные обсуждения CRTP. Todd Veldhuizen Методы для Scientific С++ - отличный ресурс для этого (1.3) и многих других передовых методов, таких как шаблоны выражений.
Кроме того, я обнаружил, что вы можете прочитать большую часть оригинальной статьи Coplien С++ Gems в книгах Google. Может быть, это все еще так.
Ответ 3
Мне пришлось искать CRTP. Однако, сделав это, я нашел кое-что из Статический полиморфизм. Я подозреваю, что это ответ на ваш вопрос.
Оказывается, что ATL использует этот шаблон довольно широко.
Ответ 4
Это В ответе Википедии есть все, что вам нужно. А именно:
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();
};
Хотя я не знаю, сколько это фактически покупает вас. Накладные расходы на вызов виртуальной функции (конечно, зависит от компилятора):
- Память: один указатель на каждую виртуальную функцию
- Время выполнения: один вызов указателя на функцию
В то время как накладные расходы на статический полиморфизм CRTP:
- Память: дублирование базы для каждого экземпляра шаблона
- Время выполнения: один вызов указателя на функцию + независимо от того, что делает static_cast