Перейти к содержанию

Стирание типов: std::function, std::any и создание собственной обёртки

Начало урока

Стирание типов: std::function, std::any и создание собственной обёртки

Скрытие конкретного типа за единым интерфейсом с семантикой значений

Главная идея: Стирание типов - это приём, при котором объект любого типа хранится за фиксированным, единым интерфейсом, так что вызывающий код больше не видит конкретный тип. std::function стирает любой вызываемый объект с заданной сигнатурой; std::any стирает любое копируемое значение. Оба дают полиморфизм времени выполнения без общего базового класса и с обычной семантикой значений - вы копируете, перемещаете и храните их как int. Внутри это дисциплинированный союз шаблонов и виртуальной диспетчеризации, и, увидев этот приём однажды, вы сможете построить свою обёртку.
К концу вы сможете:
  • объяснить, что значит "стереть" тип и почему важна семантика значений,
  • использовать std::function для хранения разнородных вызываемых объектов за одной сигнатурой,
  • использовать std::any и std::any_cast для хранения и извлечения значений неизвестного типа,
  • реализовать идиому Concept/Model, чтобы построить свою обёртку со стиранием типов,
  • рассуждать о стоимости: выделение в куче, косвенность и оптимизация малого буфера.

1. Задача: полиморфизм без базового класса

Вы уже знаете два способа писать код, работающий со многими типами. Шаблоны дают полиморфизм времени компиляции: template <class T> порождает новую функцию под каждый тип, без затрат во время выполнения, но тип проникает в каждую сигнатуру - std::vector<T> может хранить только один T. Виртуальные функции дают полиморфизм времени выполнения, но они навязчивы: каждый хранимый тип должен наследоваться от вашего базового класса и работать через указатель, поэтому вы теряете семантику значений и не можете хранить типы, которые вам не подконтрольны (например int или чужую структуру).

Стирание типов - третий путь. Оно позволяет написать один нешаблонный класс - скажем Drawable - который может хранить любой тип, предоставляющий операцию draw(), без наследования, и ведёт себя как обычное значение, которое можно скопировать в std::vector<Drawable>. Конкретный тип "стёрт": после конструирования обёртка помнит лишь как рисовать, а не что она хранит.

Аналогия - универсальная розетка. Розетка не знает и не заботится о том, что вы вставляете: лампу, зарядку ноутбука или чайник. Она предоставляет одну фиксированную форму (интерфейс), и каждое устройство подстраивается под неё. Розетка "стёрла" тип устройства: она знает лишь контракт (подавать питание), но не конкретный прибор. Обёртка со стиранием типов - это такая розетка для объектов C++.

Продолжите этот урок на YourAIZone

В полном уроке — объяснение целиком, разобранные примеры, частые ошибки, тесты с мгновенной проверкой и ИИ-репетитор. Начните сразу как гость или создайте аккаунт, чтобы сохранять прогресс.

В полном уроке

  • Объяснение целиком
  • Разобранные примеры
  • Частые ошибки и как их избежать
  • Тестовые вопросы: 15
  • ИИ-репетитор, который отвечает на вопросы по теме
  • Отслеживание прогресса для ученика, родителей и учителя