Миксины с CRTP
Составляйте поведение во время компиляции
«Миксины с CRTP» — бесплатный урок C++ Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.
Что такое примесь?
Примесь — это небольшой повторно используемый элемент поведения, который можно включить в класс. С помощью CRTP примеси добавляют функциональность во время компиляции, при этом обращаясь к классу-носителю.
Простая примесь
Эта примесь добавляет метод print_twice, который предполагает, что класс-носитель предоставляет value().
#include <iostream>
template <typename T>
struct PrintTwice {
void print_twice() const {
auto v = static_cast<const T*>(this)->value();
std::cout << v << " " << v << "\n";
}
};
struct Box : PrintTwice<Box> {
int value() const { return 7; }
};
int main() {
Box{}.print_twice();
return 0;
}Композиция нескольких примесей
Вы можете одновременно наследоваться от нескольких примесей CRTP, объединяя независимые варианты поведения в одном классе.
#include <iostream>
template <typename T>
struct Loggable {
void log() const { std::cout << "log: " << static_cast<const T*>(this)->id() << "\n"; }
};
template <typename T>
struct Resettable {
void reset() { static_cast<T*>(this)->set(0); }
};
struct Device : Loggable<Device>, Resettable<Device> {
int v = 5;
int id() const { return v; }
void set(int x) { v = x; }
};
int main() {
Device d;
d.log();
d.reset();
d.log();
return 0;
}Примеси тоже добавляют состояние
Примесь может хранить собственные члены данных, которые становятся частью компоновки составного объекта.
#include <iostream>
template <typename T>
struct Tagged {
int tag = 0;
void set_tag(int t) { tag = t; }
int get_tag() const { return tag; }
};
struct Item : Tagged<Item> {
const char* name = "item";
};
int main() {
Item it;
it.set_tag(42);
std::cout << it.name << " " << it.get_tag() << "\n";
return 0;
}Примесь и обычное наследование
Примесь CRTP отличается от обычного наследования тем, что базовый класс может обращаться вниз, к производному классу. Обычные базовые классы не могут вызывать методы, существующие только в производном типе, без виртуальных функций.
Проектирование на основе политик
Примеси позволяют использовать проектирование на основе политик: варианты поведения выбираются во время компиляции через выбор примесей, от которых выполняется наследование, подобно тому как std::vector принимает политику распределителя памяти.
Порядок и компоновка
При объединении примесей порядок базовых классов влияет на порядок конструирования и компоновку объекта. Сохраняйте примеси независимыми, чтобы порядок не влиял на корректность.
- Конструируйте в порядке объявления базовых классов
- Избегайте примесей, зависящих друг от друга
Добавление примеси равенства
Здесь примесь создаёт operator!= на основе операции равенства класса-носителя.
#include <iostream>
template <typename T>
struct EqualityMixin {
friend bool operator!=(const T& a, const T& b) { return !(a == b); }
};
struct P : EqualityMixin<P> {
int x;
P(int v) : x(v) {}
friend bool operator==(const P& a, const P& b) { return a.x == b.x; }
};
int main() {
std::cout << (P(1) != P(2)) << "\n";
return 0;
}Условные примеси
Вы можете включить примесь только при выполнении условия, объединив наследование с std::conditional и выбрав между настоящей примесью и пустой примесью.
Оптимизация пустого базового класса
Примеси, не содержащие членов данных, получают преимущество от оптимизации пустого базового класса: компилятор не выделяет для них дополнительных байтов, поэтому объединение множества примесей без состояния ничего не стоит с точки зрения размера.
#include <iostream>
template <typename T>
struct A {};
template <typename T>
struct B {};
struct C : A<C>, B<C> {
int x;
};
int main() {
std::cout << (sizeof(C) == sizeof(int)) << "\n";
return 0;
}Когда стоит использовать примеси
Примеси особенно полезны, когда многим классам нужны одинаковые независимые варианты поведения (журналирование, подсчёт, сравнение). Они помещают каждое поведение в одно место и объединяются без затрат во время выполнения.
Быстрая проверка
Проверьте, насколько хорошо Вы усвоили примеси CRTP.
Повторение
Вы научились создавать примеси с помощью CRTP.
- Примеси объединяют независимые варианты поведения во время компиляции
- Они могут обращаться к классу-носителю и хранить состояние
- Примеси без состояния ничего не стоят благодаря оптимизации пустого базового класса
- Они отлично подходят для политик журналирования, сравнения и подсчёта
Часто задаваемые вопросы
Урок «Миксины с CRTP» бесплатный?
Да — полный текст урока «Миксины с CRTP» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.
Чему я научусь в уроке «Миксины с CRTP»?
Составляйте поведение во время компиляции Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C++ Academy?
Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Миксины с CRTP»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C++ Academy?
Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.