🧬 CRTP (Curiously Recurring Template Pattern)
CRTP — это шаблон, где класс-наследник передаёт себя в качестве параметра шаблона базовому классу.
А наследник выглядит так:
⚙️ Как это работает
Когда ты вызываешь:
Базовый класс вызывает метод
при этом без виртуальных функций — всё статически известно на этапе компиляции.
🚀 Где это полезно:
— Mixin — добавление поведения к классам без виртуальности.
— Policy-based design — подмена стратегий через шаблонные параметры.
— Статический полиморфизм — альтернатива
— Ограничение интерфейса наследников — например, через
🧠 Пример Mixin с CRTP
— Без virtual
— Без динамической памяти
— Гибко и эффективно
В итоге имеем:
CRTP — способ реализовать полиморфизм, заменив виртуальные вызовы статическим связыванием на этапе компиляции.
#cpp_tips
Как использовать шаблоны, а не виртуальность, чтобы дать базовому классу знать о потомке?
CRTP — это шаблон, где класс-наследник передаёт себя в качестве параметра шаблона базовому классу.
template <typename Derived>
class Base
{
public:
void interface()
{
static_cast<Derived*>
(this)->implementation();
}
};
А наследник выглядит так:
class Derived : public Base<Derived>
{
public:
void implementation()
{
std::cout << "Derived func\n";
}
};
⚙️ Как это работает
Когда ты вызываешь:
Derived d;
d.interface();
Базовый класс вызывает метод
implementation() у конкретного типа Derived,при этом без виртуальных функций — всё статически известно на этапе компиляции.
🚀 Где это полезно:
— Mixin — добавление поведения к классам без виртуальности.
— Policy-based design — подмена стратегий через шаблонные параметры.
— Статический полиморфизм — альтернатива
virtual, но без runtime overhead.— Ограничение интерфейса наследников — например, через
enable_if и SFINAE.🧠 Пример Mixin с CRTP
template <typename Derived>
class Printable
{
public:
void print() const
{
std::cout <<
static_cast<const Derived*>
(this)->toString();
}
};
class Point : public Printable<Point>
{
public:
Point(int x, int y) : x_(x), y_(y) {}
std::string toString() const
{
return std::to_string(x_) + ","
+ std::to_string(y_);
}
private:
int x_, y_;
};
Point p(2, 3);
p.print(); // 2,3
— Без virtual
— Без динамической памяти
— Гибко и эффективно
В итоге имеем:
CRTP — способ реализовать полиморфизм, заменив виртуальные вызовы статическим связыванием на этапе компиляции.
#cpp_tips
Что выведет программа
Anonymous Quiz
50%
General
50%
Int second
0%
Ошибка компиляции
0%
Неопределенное поведение
⚙️ Type Traits: когда типы становятся логикой
Примеры Type Traits
🧠 Type Traits — шаблонные конструкции, которые предоставляют информацию о свойствах типов во время компиляции
Используются в
Примеры Type Traits
template<typename T>
struct is_pointer
{
static constexpr bool value = false;
};
template<typename T>
struct is_pointer<T*>
{
static constexpr bool value = true;
};
assert(is_pointer<int*>::value);
🧠 Type Traits — шаблонные конструкции, которые предоставляют информацию о свойствах типов во время компиляции
Используются в
std::enable_if, concepts, std::move.Какой trait определяет, является ли тип числовым?
Anonymous Poll
0%
std::is_arithmetic
0%
std::is_integral
100%
std::is_numeric
🎯 Policy-based Design: шаблонная настройка поведения
Пример реализации:
✅ Вместо наследования от суперкласса составляем объект из политик, что позволяет легко менять поведение без лишнего кода.
Пример реализации:
template<typename ThreadPolicy, typename LogPolicy>
class Engine : private ThreadPolicy, private LogPolicy
{
public:
void run()
{
this->log("Starting...");
this->lock();
this->unlock();
}
};
✅ Вместо наследования от суперкласса составляем объект из политик, что позволяет легко менять поведение без лишнего кода.
Что проще изменить в Policy-based подходе?
Anonymous Poll
0%
Интерфейс базового класса
0%
Структуру иерархии
100%
Поведение
Expression Templates
Или магия ленивых выражений
Шаблоны выражений - это метод метапрограммирования шаблонов C++, который создает структуры, представляющие вычисления во время компиляции, где выражения вычисляются только по мере необходимости для создания эффективного кода для всего вычисления.
Таким образом, шаблоны выражений позволяют программистам обойти обычный порядок вычисления на языке C++ и добиться оптимизации, такой как объединение циклов.
Пример:
🧠 Такие паттерны лежат в основе библиотек Eigen, Blitz++, Armadillo. Они предотвращают создание временных объектов — всё оптимизируется на этапе компиляции.
Или магия ленивых выражений
Шаблоны выражений - это метод метапрограммирования шаблонов C++, который создает структуры, представляющие вычисления во время компиляции, где выражения вычисляются только по мере необходимости для создания эффективного кода для всего вычисления.
Таким образом, шаблоны выражений позволяют программистам обойти обычный порядок вычисления на языке C++ и добиться оптимизации, такой как объединение циклов.
Пример:
template<typename L, typename R>
struct Add
{
L const& l; R const& r;
auto eval() const
{
return l.eval() + r.eval();
}
};
struct Value
{
double v;
auto eval() const
{
return v;
}
};
🧠 Такие паттерны лежат в основе библиотек Eigen, Blitz++, Armadillo. Они предотвращают создание временных объектов — всё оптимизируется на этапе компиляции.
Static Interface (Concepts)
Static Interface: контракты времени компиляции
🧠 Concepts — это compile-time контракты.
Компилятор гарантирует, что тип соответствует интерфейсу.
Это безопасный и быстрый способ добавления ограничений к создаваемым объектам
Static Interface: контракты времени компиляции
template<typename T>
concept Drawable = requires(T obj)
{
obj.draw();
};
template<Drawable T>
void render(T obj)
{
obj.draw();
}
🧠 Concepts — это compile-time контракты.
Компилятор гарантирует, что тип соответствует интерфейсу.
Это безопасный и быстрый способ добавления ограничений к создаваемым объектам
Две ассоциативные структуры, два разных назначения.
map
— Красно-чёрное дерево
— Операции за
O(log n)— Сохраняет сортировку ключей
unordered_map
— Хеш-таблица
— Амортизированная
O(1)— Нет сортировки
Но, может переполняться и ребалансироваться
std::unordered_map<std::string, int> id;
id["Alice"] = 5;
💡 Если нужны сортированные ключи — map. Если нужна скорость — unordered_map.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Что делает map.lower_bound(k)?
Anonymous Poll
25%
Возвращает все итераторы на элементы ≤ k
0%
Возвращает итератор на элемент с ключом ≤ k
75%
Возвращает итератор на первый элемент ≥ k
STL-алгоритмы работают над итераторами, а не над контейнерами — поэтому они универсальны.
std::vector<int> v = {1, 2, 3, 4};
auto it = std::find(v.begin(), v.end(), 3);
std::reverse(v.begin(), v.end());
std::sort(v.begin(), v.end());Полезные алгоритмы, которые могут быть полезны:
std::partition — разбивает по условию
std::unique — убирает дубликаты (но не меняет размер!)
std::any_of, std::none_of, std::all_of
💡 Комбинируя алгоритмы и лямбды, можно писать универсальный и масштабируемый код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Современный C++ даёт инструменты для более безопасного кода:
std::optional
Хранит “значение или отсутствие значения”.
std::optional<int> try_parse(const std::string& s);
std::variant
Безопасный
union, работающий через типы.variant<int, string> v = "hello";
std::any
Хранит любое значение, но требует приведения типа.
Please open Telegram to view this post
VIEW IN TELEGRAM
Что делает std::visit?
Anonymous Poll
43%
Проверяет variant на тип
57%
Вызывает функцию в зависимости от активного типа
0%
Переводит variant в string
👍4
std::deque — гибрид между std::vector и std::list:
— Быстрые вставки в начало и конец
— Почти такой же быстрый доступ по индексу, как у vector
— Не хранит данные в одном непрерывном блоке
std::deque<int> d = {1, 2, 3};
d.push_front(0);
d.push_back(4);Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
std::string_view — неблокирущий, не владеющий срез строки.
— Дешёвый аргумент, парсер, логирование
void log(std::string_view msg) {
std::cout << msg;
}❗️Частая ошибка:
std::string_view sv = std::string("temp"); // UB🎯 Используйте в сигнатурах вместо const std::string&.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4