C++                
            Любопытно повторяющийся шаблон шаблона (CRTP)
        
        
            
    Поиск…
Вступление
Шаблон, в котором класс наследует шаблон шаблона сам по себе как один из его параметров шаблона. CRTP обычно используется для обеспечения статического полиморфизма в C ++.
Любопытно повторяющийся шаблон шаблона (CRTP)
 CRTP - это мощная статическая альтернатива виртуальным функциям и традиционному наследованию, которые могут использоваться для предоставления свойств типов во время компиляции. Он работает, имея шаблон базового класса, который принимает в качестве одного из своих параметров шаблона производный класс. Это позволяет ему юридически выполнить static_cast его this указателя на производный класс. 
Разумеется, это также означает, что класс CRTP всегда должен использоваться как базовый класс другого класса. И производный класс должен перейти к базовому классу.
 Допустим, у вас есть набор контейнеров, которые поддерживают функции begin() и end() . Требования к стандартной библиотеке для контейнеров требуют большей функциональности. Мы можем создать базовый класс CRTP, который обеспечивает эту функциональность, основанную исключительно на begin() и end() : 
#include <iterator>
template <typename Sub>
class Container {
  private:
    // self() yields a reference to the derived type
    Sub& self() { return *static_cast<Sub*>(this); }
    Sub const& self() const { return *static_cast<Sub const*>(this); }
  public:
    decltype(auto) front() {
      return *self().begin();
    }
    decltype(auto) back() {
      return *std::prev(self().end());
    }
    decltype(auto) size() const {
      return std::distance(self().begin(), self().end());
    }
    decltype(auto) operator[](std::size_t i) {
      return *std::next(self().begin(), i);
    }
};
 Вышеупомянутый класс предоставляет функции front() , back() , size() и operator[] для любого подкласса, который предоставляет begin() и end() . Примером подкласса является простой динамически распределенный массив: 
#include <memory>
// A dynamically allocated array
template <typename T>
class DynArray : public Container<DynArray<T>> {
  public:
    using Base = Container<DynArray<T>>;
    DynArray(std::size_t size)
      : size_{size},
      data_{std::make_unique<T[]>(size_)}
    { }
    T* begin() { return data_.get(); }
    const T* begin() const { return data_.get(); }
    T* end() { return data_.get() + size_; }
    const T* end() const { return data_.get() + size_; }
  private:
    std::size_t size_;
    std::unique_ptr<T[]> data_;
};
 Пользователи класса DynArray могут легко использовать интерфейсы, предоставляемые базовым классом CRTP, следующим образом: 
DynArray<int> arr(10);
arr.front() = 2;
arr[2] = 5;
assert(arr.size() == 10);
Полезность. Этот шаблон, в частности, избегает вызовов виртуальных функций во время выполнения, которые происходят, чтобы пересечь иерархию наследования и просто полагается на статические приведения:
DynArray<int> arr(10);
DynArray<int>::Base & base = arr;
base.begin(); // no virtual calls
 Единственный статический бросок внутри функции begin() в базовом классе Container<DynArray<int>> позволяет компилятору радикально оптимизировать код, и при просмотре виртуальной таблицы не происходит во время выполнения. 
 Ограничения: поскольку базовый класс шаблонизирован и отличается для двух разных DynArray s, невозможно сохранить указатели на их базовые классы в однородном по типу массиве, как обычно можно было бы сделать с обычным наследованием, когда базовый класс не зависит от производного тип: 
class A {};
class B: public A{};
A* a = new B;
CRTP, чтобы избежать дублирования кода
Пример в шаблоне посетителей представляет собой убедительный прецедент для CRTP:
struct IShape
{
    virtual ~IShape() = default;
    virtual void accept(IShapeVisitor&) const = 0;
};
struct Circle : IShape
{
    // ...        
    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
    // ...
};
struct Square : IShape
{
    // ...    
    // Each shape has to implement this method the same way
    void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
    // ...
};
 Каждому дочернему типу IShape необходимо реализовать ту же функцию одинаково. Это много лишнего набора текста. Вместо этого мы можем ввести новый тип в иерархию, который делает это для нас: 
template <class Derived>
struct IShapeAcceptor : IShape {
    void accept(IShapeVisitor& visitor) const override {
        // visit with our exact type
        visitor.visit(*static_cast<Derived const*>(this));
    }
};
И теперь каждая форма просто должна унаследовать от акцептора:
struct Circle : IShapeAcceptor<Circle>
{
    Circle(const Point& center, double radius) : center(center), radius(radius) {}
    Point center;
    double radius;
};
struct Square : IShapeAcceptor<Square>
{
    Square(const Point& topLeft, double sideLength) : topLeft(topLeft), sideLength(sideLength) {}    
    Point topLeft;
    double sideLength;
};
Не требуется дублировать код.