Skip to content

Latest commit

 

History

History
60 lines (45 loc) · 4.51 KB

README.md

File metadata and controls

60 lines (45 loc) · 4.51 KB

function

Класс function представляет собой полиморфную оболочку для функциональных объектов, а именно для тех, чей тип удовлетворяет Callable и CopyConstructible. Это может быть указатель на функцию, лямбда, класс с перегруженным operator() и т.д. Хранимый функциональный объект в терминах стандарта принято называть target.

Напоминание идеи с лекции

Храним полем unique_ptr<concept>, где concept — класс с виртуальными функциями (например, operator()). При конструировании из функционального объекта типа F создаём в динамической памяти model<F>, где хранится сам F, а также определены те самые виртуальные функции.

Small-object optimization

Функциональные объекты часто не имеют состояния вообще, или имеют, но небольшое. Поэтому возникает естественное желание легковесные объекты хранить непосредственно внутри function, вместо указателя на динамическую память.

К сожалению, нам больше не подойдёт unique_ptr, ведь в случае small object не будет указателя на динамическую память. Во-первых, не очень понятно, какой у такого unique_ptr'а должен быть Deleter. Во-вторых, а как тогда правильно мувать small object'ы?

Идея реализации

Разделим хранилище и логику на два поля:

  • выровненный массив байт, где будет лежать либо сам функциональный объект (в случае small object), либо указатель на него;
  • указатель на дескриптор — набор вспомогательных функций для оперирования хранилищем (может быть выражен классом с виртуальными функциями).

В зависимости от типа функционального объекта, будем хранить указатели на дескрипторы с разными динамическими типами.

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

Прочие требования

Дефолтный конструктор создаёт пустой (empty) function, который не хранит в себе никакой функциональный объект, а при вызове operator() кидает bad_function_call. Для него удобно сделать отдельный дескриптор.

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

Метод F* target<F>() должен возвращать указатель на текущий функциональный объект, если его динамический тип совпадает с F, и nullptr иначе.