CRTP, чтобы избежать динамического полиморфизма - PullRequest
87 голосов
/ 04 ноября 2008

Как я могу использовать CRTP в C ++, чтобы избежать накладных расходов на виртуальные функции-члены?

Ответы [ 4 ]

133 голосов
/ 04 ноября 2008

Есть два пути.

Первый - указать интерфейс статически для структуры типов:

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

Таким образом, объединение определения структуры / интерфейса и вывода типа времени компиляции в ваших функциях позволяет вам выполнять статическую диспетчеризацию вместо динамической диспетчеризации. В этом суть статического полиморфизма.

18 голосов
/ 04 ноября 2008

Я сам искал достойных обсуждений CRTP. Техника для C ++ Тодда Велдхейзена - отличный ресурс для этого (1.3) и многих других передовых методов, таких как шаблоны выражений.

Кроме того, я обнаружил, что вы можете прочитать большую часть оригинальной статьи Коплиена о C ++ Gems в книгах Google. Может быть, это все еще так.

1 голос
/ 04 ноября 2008

Мне пришлось поискать CRTP . Сделав это, я нашел кое-что о статическом полиморфизме . Я подозреваю, что это ответ на ваш вопрос.

Оказывается, ATL использует этот шаблон довольно широко.

0 голосов
/ 04 ноября 2008

В этом ответе из Википедии есть все, что вам нужно. А именно:

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
...