Основная цель функтора - отделить код, который знает, как выполнить какую-то работу, от кода, который знает, когда эта работа должна быть выполнена (классический пример - ассоциирование функтора с кнопкой пользовательского интерфейса).
Одним небольшим преимуществом модели функторов является то, что простые старые указатели на функции уже являются функторами.Никакой дополнительной работы не требуется, чтобы обернуть их.Я считаю это незначительным преимуществом, потому что а) указатель на функцию немного менее эффективен, чем перенос прямого вызова функции, и б) я обнаружил, что мне почти всегда нужно привязывать какую-либо форму состояния к тому, что я переносу, даже еслиэто просто указатель this
функции-члена.
Ключевое преимущество унарного интерфейса состоит в том, что он служит языком общения для производителей и потребителей функторов.Вы могли бы, скажем, определить функторы, чтобы у всех была функция-член invoke()
, но тогда другая толпа решила бы стандартизировать do()
, а другая могла бы пойти на call()
.И все эти решения включают в себя больше ввода.
Кроме того, множественные функции-члены в одном «функторе» никогда строго не требуются.Если некоторый код должен вызывать несколько различных операций, вы можете просто передать несколько функторов.Это обеспечивает хорошую гибкость, поскольку операции могут быть связаны, или они могут быть совершенно не связаны между собой.
Разделенным примером является хеш-таблица, для которой требуется компаратор равенства и хеш-функция.В этом случае две функции могут быть не связаны: обернуть класс operator==()
для равенства и обернуть свободную функцию для вычисления хеша.
Связанный пример - это компонент пользовательского интерфейса, который генерирует несколько различных событий.Один класс может отвечать на все события, или разные классы могут отвечать на разные группы событий.Функторы облегчают выбор любой модели, в то время как требование одного «интерфейса», который определяет обратные вызовы для всех событий компонента, более неудобно.Функторы также упрощают задачу, если один объект хочет по-разному обрабатывать события из двух компонентов, поскольку вы можете назначить каждому компоненту разный набор функций-членов, обернутых в функторы.
Наконец, обернуть существующую функциональность в функторхорошо поняты и широко поддерживаются библиотеками, такими как boost.bind, тогда как создание одноразовых классов, реализующих doX()
и doY()
, - нет.Кроме того, новый стандарт добавляет лямбды, которые значительно упрощают создание функторов.