CRTP против прямой реализации функции в «производной» - PullRequest
4 голосов
/ 30 августа 2011

Я пытаюсь лучше понять CRTP.До сих пор я понимаю, что он позволяет писать функции, подобные следующим:

template <class T>
void foo(Base<T> x ) { x.do_stuff() }

Теперь, в зависимости от фактического объекта времени компиляции x, который передается в функцию foo(), онбудет делать разные вещи.

Однако я мог бы получить класс Derived из Base и скрыть / скрыть его do_stuff() не виртуальным, но переопределенным Derived::do_stuff.Поэтому, когда именно правильно использовать CRTP, скорее самый простой нетривиальный пример, который демонстрирует преимущество CRTP перед затенением / маскированием.

1 Ответ

6 голосов
/ 30 августа 2011

Смысл CRTP в том, чтобы иметь возможность получать тип производного объекта без виртуальности. Если вы делаете

struct B { void foo() const; }
struct D : B { void foo() const; }

void bar(const B& x) { x.foo(); }

затем bar вызывает B::foo, а не D::foo при передаче объекта D, поскольку foo не является виртуальной функцией. Если вы хотите, чтобы D::foo вызывался, то вам нужны либо виртуальные функции, либо CRTP.

С простейшим типом CRTP:

template <typename>
struct B { void foo() const; }

struct D : B<D> { void foo() const; }

template <typename T>
void bar(const B<T>& x)
{
    static_cast<const T&>(x).foo();
}

это вызывает D::foo(), когда вы передаете объект bar D.

Альтернативный трюк CRTP, который, однако, заставляет D предоставить реализацию для foo, это

template <typename T>
struct B
{
    void foo() const { static_cast<const T*>(this)->foo_impl(); }
    // default implementation if needed
    // void foo_impl() const { ... }
};

struct D : B<D> { void foo_impl() const { ... } };

template <typename T>
void bar(const B<T>& x) { x.foo(); }

но вам все еще нужен параметр шаблона для B (чтобы foo отправлялся правильно) и, следовательно, функция bar шаблона.

Кроме того, если вы не используете CRTP, вам лучше иметь виртуальный деструктор, который может добавить нежелательные издержки для легких классов, которые должны быть полностью встроенными. С помощью CRTP вы просто напишите защищенный деструктор (приватный деструктор + friend T в C ++ 0x).

...