TGViewer
this->notes. this->notes. @thisnotes · 4.52K subscribers
Post #268 3.72K
#cpp

С Новым Годом🥳 Начнём 2024й с простенького.

Curiously recurring template pattern.

CRTP -- типичный подход к реализации статического полиморфизма.

Давайте посмотрим на реализацию:

template <typename Child>
struct Parent {
int SomeFunc() {
return static_cast<Child*>(this)->SomeFunc();
}
};

struct Child : Parent<Child> {
int SomeFunc() { return 228; }
};

Тут мы завели родителя, который предоставляет некоторый интерфейс и умеет вызывать метод у ребёнка, кастуя себя к его типу. А вот у ребёнка происходит наследование от родителя, которого мы параметризовали этим самым ребёнком. Потому этот подход так и называется.

static_cast к указателю на ребёнка из this называется "Barton and Nickman trick".


Ну и дальше можем использовать наше решение:

Parent* p = new Child;
std::cout << p->SomeFunc();

Из плюсов мы больше не тратим в рантайме время на то, чтобы прыгать по виртуальным таблицам и поиск нужного метода нужного ребёнка для вызова. Тут есть статья про реальную стоимость виртуальных вызовов, так что иногда это может быть полезной оптимизацией: The true price of virtual functions in C++.

С другой стороны, очевидно, вы не можете класть несколько таких детей в один вектор. Потому что родители разных детей с точки зрения компилятора это разные типы:

std::vector<Parent<Child>> v;

Другой тип тут нормально не положишь.

Пользоваться этим нужно аккуратно. Например в реализации выше следующий код -- ub:

// Parent<Child> p; p.SomeFunc(); // CE!

А ещё легко словить проблемы, если вдруг вы отнаследуете разные классы от родителя с одним и тем же параметром в шаблоне.

И концептуально важный момент. Исходя из того, как устроен подход, это эмуляция наследования, когда ребёнок расширяет логику родителя (а не наоборот). Так что архитектурно этот подход не везде применим.

Где это используется?

Кроме кейсов выше популярна задача написания "миксинов" -- специальных классов, которые расширяют логику работы класса какими-то конкретными методами. Вообще концептуально подразумевается, что от миксинов не наследуются, а скорее они каким-то образом "инклудятся" в классы, но в плюсах (и других языках, например python), такие вещи всё же реализуют через наследование.

Из примеров подобного -- std::enable_shared_from_this. Представим, что мы хотим сделать метод, который будет возвращать std::shared_ptr на наш класс:

struct bad {
std::shared_ptr<bad> get() {
return std::shared_ptr<bad>(this);
}
};

При каждом вызове этого метода будет создаваться новая компонента связности шеред птров, из-за чего будет как минимум double-free. Корректно сделать так:

struct good : std::enable_shared_from_this<good> {
std::shared_ptr<good> get() {
return shared_from_this(); // from enable_shared_from_this
}
};

Обычно такую структуру реализуют с std::weak_ptr внутри, который и раздаёт std::shared_ptr.

Можно продолжить в этом направлении и реализовать, например,
- миксин, который будет добавлять operator>, если у класса есть operator<;
- миксин, при наследовании от которого у вас автоматически считается количество живых в данный момент программы экземпляров ребёнка.

(это простые задачки, так что оставим упражнением).

Для первого даже в буст либу завезли (boost::operators), которая позволяет избежать бойлерплейта при написании операторов.

Тут можно посмотреть больше примеров использования CRTP.

============================

Хорошего года, ребят🥳🥳🥳
  • 👍 29
  • 🍌 9
  • ❤ 6
  • 🔥 2
  • 🥰 2
  • 👎 1
  • 🗿 1
More from @thisnotes
  1. Sep 17, 2026#common Сидите вы себе спокойно, разрабатываете поиск каких-нибудь объектов. Может это тов…
  2. Sep 9, 2026#cpp #books Да, книга 2001ого года. Мы ровесники. И да, в ней в основном обсуждаются какие…
  3. Sep 2, 2026#perf Попробовал собрать в кучку (кажется, немного сумбурно всё же) мысли по двум моментам…
  4. Aug 31, 2026Давайте новый тег заведём: #perf Во-первых, надо понять, что я вообще понимаю под перфом,…
  5. Aug 27, 2026#common Мы часто делаем системы, которые обладают какими-то ограничениями. Ограничения наш…
  6. Aug 24, 2026#list 0. [talk] Achieving Peak Performance for Matrix Multiplication in C++. Aliaksei Sala…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →