Что такое функторы в c
Небольшое исследование по использованию функторов в стандартной библиотеке STL С++
Это статья для начинающих. Рассматривается использование простых функторов в алгоритмах. Рассказано про копирующий конструктор. Основная цель, это исследование количества создаваемых объектов функторов и простых методов как это можно сделать. Программа-пример последовательно усложняется. Некоторые шаги могут показаться неверными и лишними, но это типичный процесс исследования и отладки. Такой подход выбран сознательно. Обычный способ, когда делаются только разумные шаги, далек от реальности. И еще, чтобы заинтриговать, скажу, что в конце статьи получаем очень неожиданный результат.
Итак, начнем.
Для простоты считаем, что пространство имен std добавлено:
Допустим, у нас есть контейнер с объектами. В нашем случае это вектор с интами.
И мы хотим его распечатать. Можно сделать так:
или лучше с итераторами:
более правильно, но длиннее.
Но лучше не использовать обычный цикл for, а взять алгоритм for_each, который сам перебирает элементы контейнера и вызывает для каждого заданную функцию. Это позволит писать более наглядный код. Алгоритм for_each принимает на вход начало и конец диапазона и функцию.
В качестве такой функции будем использовать функтор, который будет делать вывод. Функтор это объект, который используется как функция. Звучит страшно, но на самом деле просто. В данном случае, функтор представляет собой класс в котором реализован оператор скобки operator().
Оператор будет принимать в качестве параметра объект для обработки. Теперь алгоритм можно вызвать так:
Красиво и аккуратно. Что при этом происходит? Мы создаем безымянный объект Print, который передаем в функцию for_each, которая перебирает элементы с первого до последнего и передает их функции operator().
Вся программа будет выглядеть так:
Всё просто. А вот теперь вопрос, сколько объектов класса Print будет создано? Можно предположить, что один, при создании безымянного объекта командой Print().
Добавляем конструктор, деструктор и отладочный вывод, чтобы увидеть процесс создания и удаления объектов. Теперь класс будет выглядеть так:
Как интересно. Один конструктор, и два деструктора. Как так может быть? Если вызвать деструктор для уже удаленного объекта, будет непредсказуемое поведение. Такого не может быть. Попробуем разобраться. И вот тут надо вспомнить, что объект может быть создан не только обычным образом, но и с использованием копирующего конструктора. Это конструктор, который создаёт новый объект, как копию переданного.
В данном случае наш объект не содержит никаких данных, поэтому мы ничего не копируем. Но надо помнить, если мы замещает стандартный копирующий конструктор своим, то вся ответственность ложится на нас.
Весь код:
Ну вот, стало лучше, два конструктора, два деструктора. Но вот почему объектов два? Про первый объект понятно, он создается в нашем коде. В вот второй создается даже после того, как отрабатывает весь вывод. Зачем?
Посмотрим описание функции for_each:
Вот, функция возвращает функтор, который мы никак не использовали. Добавим получение результата.
Вывод совсем не изменился. То есть это и была невидимая переменная, которую нам передавали, но мы игнорировали.
Давайте сделаем нечто похожее, но с алгоритмом transform. Это алгоритм перебирает элементы одного контейнера, преобразует их и помещает в другой контейнер. Мы сделаем умножение на 10 и результат поместим обратно в исходный контейнер.
Ну, всё понятно, transform не возвращает никаких функторов, поэтому создается только один временный объект.
А вот теперь самое интересное:
Алгоритм sort
Вопрос, сколько объектов типа Compare будет создано? Один? А вот и нет. Сделаем вектор из трех элементов и отсортируем. Нам надо будет создать функтор, который осуществляет сравнение двух объектом, это оператор меньше operator
Я продолжаю много слушать о функторах на С++. Может ли кто-нибудь дать мне обзор относительно того, что они есть и в каких случаях они будут полезны?
ОТВЕТЫ
Ответ 1
Есть несколько приятных вещей о функторах. Во-первых, в отличие от обычных функций, они могут содержать состояние. В приведенном выше примере создается функция, которая добавляет 42 к тому, что вы ей даете. Но это значение 42 не является жестко запрограммированным, оно было указано как аргумент конструктора, когда мы создали наш экземпляр functor. Я мог бы создать еще один сумматор, добавив 27, просто вызвав конструктор с другим значением. Это делает их красиво настраиваемыми.
Если бы я передал указатель на функцию, компилятор не смог сразу увидеть, на какую функцию он указывает, поэтому, если он не выполняет некоторые довольно сложные глобальные оптимизации, ему придется разыгрывать указатель во время выполнения, а затем сделать звоните.
Ответ 2
и вы можете использовать boost:: bind для добавления состояния к этому функтору
и наиболее полезно, с boost:: bind и boost:: function вы можете создать functor из метода класса, на самом деле это делегат:
Вы можете создать список или вектор функторов
Есть одна проблема со всем этим, сообщения об ошибках компилятора не читаются человеком:)
Ответ 3
Реальное преимущество заключается в том, что функтор может удерживать состояние.
Ответ 4
Название «functor» традиционно используется в теория категорий задолго до появления на сцене С++. Это не имеет ничего общего с концепцией функтора С++. Лучше использовать имя объект функции вместо того, что мы называем «функтором» на С++. Вот как другие языки программирования называют подобные конструкции.
Используется вместо простой функции:
Используется вместо указателя на функцию:
Используется вместо виртуальной функции:
Ответ 5
Как и многие другие, функтор является объектом, который действует как функция, то есть перегружает оператор вызова функции.
Затем вы можете передать объект MultiplyBy на такой алгоритм, как std:: transform:
Ответ 6
Для новичков, подобных мне среди нас: после небольшого исследования я выяснил, что сделал код jalf, сделанный.
Чтобы создать функтор сначала, вы создаете свой класс. Затем вы создаете конструктор класса с параметром вашего выбора типа и имени. Это следует в том же самом заявлении в списке инициализаторов (в котором используется один оператор двоеточия, что-то, к чему я также был добавлен), который строит объекты-члены класса с ранее объявленным параметром для конструктора. Затем () operator перегружен. Наконец, вы объявляете частные объекты созданного вами класса или структуры.
Мой код (я искал имена переменных jalf)
Если какое-либо из этого является неточным или просто неправильно, не стесняйтесь поправлять меня!
Ответ 7
Вот простой пример, который преобразует тип в double :
Ответ 8
Вот реальная ситуация, когда я был вынужден использовать Functor для решения моей проблемы:
У меня есть набор функций (скажем, 20 из них), и все они одинаковы, за исключением того, что каждый вызывает другую специфическую функцию в трех конкретных точках.
Это невероятные отходы и дублирование кода. Обычно я бы просто передал указатель на функцию и просто назову это в трех точках. (Так что код должен появляться только один раз, а не двадцать раз.)
Но потом я понял, что в каждом случае для конкретной функции требуется совершенно другой профиль параметров! Иногда 2 параметра, иногда 5 параметров и т.д.
Другим решением будет иметь базовый класс, где конкретная функция является переопределенным методом в производном классе. Но действительно ли я хочу построить все это НАСЛЕДОВАНИЕ, так что я могу передать указатель функции.
РЕШЕНИЕ: Итак, что я сделал, я создал класс-оболочку ( «Functor» ), который может вызвать любую из функций, которые мне нужны. Я устанавливаю его заранее (с его параметрами и т.д.), А затем передаю его вместо указателя на функцию. Теперь вызываемый код может вызвать функтор, не зная, что происходит внутри. Он может даже называть его несколько раз (мне нужно было позвонить 3 раза.)
Ответ 9
Кроме использования в обратном вызове, С++-функторы также могут помочь обеспечить стиль доступа Matlab к классу матрицы. Существует пример.
Ответ 10
Функторы используются в gtkmm для подключения некоторой кнопки GUI к фактической функции или методу С++.
Таким образом, вы не можете запускать (простым способом) методы из своего класса в потоке, не делая ничего лишнего.
Ответ 11
Чтобы добавить, я использовал объекты функции, чтобы соответствовать существующему унаследованному методу шаблону команды; (только место, где красота ОО-парадигмы истинная ОКР, которую я ощущал); Также добавьте шаблон привязки соответствующих функций.
Предположим, что ваш метод имеет подпись:
Примечание. Это уродливо, и, возможно, вы можете использовать помощники связывания Boost и т.д., но если вы не можете или не хотите, это один из способов.
Кроме того, нам нужен вспомогательный метод mem_fun3 для вышеупомянутого класса, чтобы помочь в вызове.
Теперь, чтобы привязать параметры, мы должны написать функцию связывания. Итак, вот оно:
Теперь мы должны использовать это с классом Command; используйте следующую команду typedef:
Вот как вы это называете:
Примечание: f3(); вызовет метод task1- > ThreeParameterTask (21,22,23);.
Полный контекст этого шаблона на следующей ссылке
Ответ 12
Они наиболее полезны для ситуаций, в которых вам необходимо связать некоторые данные с повторными или задержанными вызовами функции.
Например, связанный список функторов может использоваться для реализации базовой системы с синхронным сопроцессором с низким уровнем накладных расходов, диспетчера задач или прерывания анализа файлов. Примеры:
Конечно, эти примеры не так полезны сами по себе. Они показывают только, как функторы могут быть полезны, сами функторы очень простые и негибкие, и это делает их менее полезными, чем, например, то, что обеспечивает boost.
Ответ 13
Функтор также может использоваться для имитации определения локальной функции внутри функции. Обратитесь к question и another.
Но локальный функтор не может получить доступ к внешним автоматическим переменным. Функция лямбда (С++ 11) является лучшим решением.
Ответ 14
Большое преимущество реализации функций как функторов заключается в том, что они могут поддерживать и повторно использовать состояние между вызовами. Например, многие алгоритмы динамического программирования, такие как алгоритм Вагнера-Фишера для вычисления Левенштейн расстояние между строками, работа, заполняя большую таблицу результатов. Очень неэффективно распределять эту таблицу каждый раз, когда вызывается функция, поэтому реализация функции как функтора и превращение таблицы в переменную-член может значительно повысить производительность.
Ответ 15
Что такое функторы C++ и их использование?
Я постоянно слышу много о функторах в C++. Может ли кто-нибудь дать мне общее представление о том, что это такое и в каких случаях они могут быть полезны?
15 ответов
Функтор-это в значительной степени просто класс, который определяет operator(). Это позволяет создавать объекты, которые «look like» являются функцией:
Есть несколько приятных вещей о функторах. Во-первых, в отличие от обычных функций, они могут содержать состояние. В приведенном выше примере создается функция, которая добавляет 42 к тому, что вы ей даете. Но это значение 42 не жестко закодировано, оно было указано в качестве аргумента конструктора, когда мы создавали наш экземпляр функтора. Я мог бы создать еще один сумматор, который добавил бы 27, просто вызвав конструктор с другим значением. Это делает их хорошо настраиваемыми.
Если бы вместо этого я передал указатель на функцию, компилятор не смог бы сразу увидеть, на какую функцию он указывает, поэтому, если он не выполняет некоторые довольно сложные глобальные оптимизации, ему пришлось бы разыменовать указатель во время выполнения, а затем выполнить вызов.
Небольшое дополнение. Вы можете использовать boost::function для создания функторов из функций и методов, например:
и вы можете использовать boost::bind для добавления состояния к этому функтору
и самое полезное, с помощью boost::bind и boost::function вы можете создать функтор из метода класса, на самом деле это делегат:
Вы можете создать список или вектор функторов
Есть одна проблема со всем этим материалом, сообщения об ошибках компилятора не читаются человеком 🙂
Реальное преимущество заключается в том, что функтор может удерживать состояние.
Имя «functor» традиционно использовалось в теории категорий задолго до появления C++. Это не имеет ничего общего с концепцией функтора C++. Лучше использовать объект функции name вместо того, что мы называем «functor» в C++. Именно так другие языки программирования называют подобные конструкции.
Используется вместо простой функции:
Используется вместо указателя функции:
Используется вместо виртуальной функции:
Затем вы можете передать объект MultiplyBy алгоритму типа std::transform:
Для новичков вроде меня среди нас: после небольшого исследования я выяснил, что делает код, опубликованный джалфом.
Чтобы создать функтор, сначала вы создаете свой класс. Затем вы создаете конструктор класса с выбранным вами параметром типа и имени. За этим в том же операторе следует список инициализаторов (который использует один оператор двоеточия, что я тоже был новичком), который создает объекты-члены класса с ранее объявленным параметром конструктора. Тогда () operator перегружается. Наконец, вы объявляете частные объекты класса или структуры, которые вы создали.
Мой код (я нашел имена переменных jalf запутанными)
Если что-то из этого неточно или просто неправильно, не стесняйтесь поправлять меня!
Вот простой пример, который преобразует тип в double :
Есть два закона, которым должны следовать функторы. Первый-это закон тождества, который гласит, что если функтору дана функция тождества, то это должно быть то же самое, что применение функции тождества к типу, то есть fmap(identity, x) должно быть то же самое, что identity(x) :
Следующий закон-это закон композиции, который гласит, что если функтору дана композиция из двух функций, то это должно быть то же самое, что применить функтор для первой функции, а затем снова для второй функции. Таким образом, fmap(std::bind(f, std::bind(g, _1)), x) должен быть таким же, как fmap(f, fmap(g, x)) :
Вот реальная ситуация, когда я был вынужден использовать функтор для решения моей проблемы:
У меня есть набор функций (скажем, 20 из них), и все они идентичны, за исключением того, что каждая вызывает другую конкретную функцию в 3 определенных местах.
Это невероятное расточительство и дублирование кода. Обычно я просто передаю указатель функции и просто вызываю его в 3 местах. (Таким образом, код должен появиться только один раз, а не двадцать.)
Но потом я понял, что в каждом конкретном случае конкретная функция требует совершенно другого профиля параметров! Иногда 2 параметра, иногда 5 параметров и т. д.
Другим решением было бы иметь базовый класс, где конкретная функция является переопределенным методом в производном классе. Но действительно ли я хочу построить все это INHERITANCE только для того, чтобы передать указатель функции.
SOLUTION: Итак, я сделал класс-оболочку (a «Functor»), который может вызывать любую из необходимых мне функций. Я настраиваю его заранее (с его параметрами и т. д.), а затем передаю его вместо указателя функции. Теперь вызываемый код может вызвать функтор, не зная, что происходит внутри. Он даже может позвонить ему несколько раз (мне нужно было позвонить 3 раза.)
Вот он-практический пример, когда функтор оказался очевидным и простым решением, которое позволило мне сократить дублирование кода с 20 функций до 1.
Функторы используются в gtkmm для подключения некоторой кнопки GUI к фактической функции или методу C++.
Таким образом, вы не можете запускать (простым очевидным способом) методы из вашего класса в потоке, не делая чего-то дополнительного.
Они наиболее полезны в ситуациях, когда необходимо связать некоторые данные с повторными или отложенными вызовами функции.
Например, связанный список функторов может быть использован для реализации базовой синхронной сопрограммной системы с низкими накладными расходами, диспетчера задач или прерываемого синтаксического анализа файлов. Примеры:
Конечно, сами по себе эти примеры не так уж полезны. Они только показывают, как функторы могут быть полезны, сами функторы очень просты и негибки, и это делает их менее полезными, чем, например, то, что предоставляет boost.
Большим преимуществом реализации функций как функторов является то, что они могут поддерживать и повторно использовать состояние между вызовами. Например, многие алгоритмы динамического программирования, такие как алгоритм Вагнера-Фишера для вычисления расстояния Левенштейна между строками, работают путем заполнения большой таблицы результатов. Очень неэффективно выделять эту таблицу каждый раз, когда вызывается функция, поэтому реализация функции как функтора и превращение таблицы в переменную-член может значительно повысить производительность.
Чтобы добавить это, я использовал функциональные объекты, чтобы подогнать существующий унаследованный метод к шаблону команды; (единственное место, где красота OO парадигмы истинного OCP я чувствовал ); Также добавим сюда соответствующий шаблон адаптера функции.
Предположим, что ваш метод имеет сигнатуру:
Примечание-это некрасиво, и, возможно, вы можете использовать помощники привязки Boost и т. д., Но если вы не можете или не хотите, это один из способов.
Кроме того, нам нужен вспомогательный метод mem_fun3 для вышеуказанного класса, чтобы помочь в вызове.
Теперь, чтобы связать параметры, мы должны написать функцию связывания. Итак, вот оно:
Теперь мы должны использовать это с классом Command; используйте следующий typedef:
Вот как вы это называете:
Полный контекст этого паттерна можно найти по следующей ссылке
Но локальный функтор не может получить доступ к внешним автоматическим переменным. Функция lambda (C++11) является лучшим решением.
У меня есть «discovered» очень интересное использование функторов: я использую их, когда у меня нет хорошего имени для одного метода, так как функтор-это метод без имени 😉
Урок №139. Перегрузка оператора ()
Обновл. 25 Сен 2021 |
На этом уроке мы рассмотрим перегрузку оператора () в языке С++.
Перегрузка оператора ()
Но следует помнить о двух вещах:
Во-первых, перегрузка круглых скобок должна осуществляться через метод класса.
Во-вторых, в не объектно-ориентированном C++ оператор () является оператором вызова функции. В случае с классами перегрузка круглых скобок выполняется в методе operator()()<> (в объявлении функции перегрузки находятся две пары круглых скобок).
Матрицы являются ключевой концепцией в линейной алгебре и часто используются в геометрическом моделировании и в 3D-графике. Всё, что вам нужно знать сейчас — это то, что класс Matrix является двумерным массивом (5×5 типа double).
На уроке о перегрузке оператора индексации мы использовали оператор [] для прямого доступа к элементам закрытого одномерного массива. Здесь же нам нужен доступ к элементам двумерного массива. Поскольку оператор [] ограничен лишь одним параметром, то его функциональности недостаточно для доступа к двумерному массиву.
Однако, поскольку оператор () может принимать разное количество параметров, мы можем объявить версию operator(), которая будет принимать два целочисленных параметра (два индекса), и использовать эти индексы для доступа к элементам нашего двумерного массива. Например:
Результат выполнения программы:
Выполним перегрузку оператора () еще раз, но уже без использования каких-либо параметров:
Результат выполнения программы:
Функторы в C++
Перегрузка оператора () используется в реализации функторов (или «функциональных объектов») — классы, которые работают как функции. Преимущество функтора над обычной функцией заключается в том, что функторы могут хранить данные в переменных-членах (поскольку они сами являются классами). Вот пример использования простого функтора:
Обратите внимание, использование класса Accumulator выглядит так же, как и вызов обычной функции, но наш объект класса Accumulator может хранить значение, которое увеличивается.
Вы можете спросить: «Зачем использовать класс, если всё можно реализовать и через обычную функцию со статической локальной переменной?». Можно сделать и через static, но, поскольку функции представлены только одним глобальным экземпляром (т.е. нельзя создать несколько объектов функции), использовать эту функцию мы можем только для выполнения чего-то одного за раз. С помощью функторов мы можем создать любое количество отдельных функциональных объектов, которые нам нужны, и использовать их одновременно.
Заключение
Перегрузка оператора () также часто используется при создании функторов. Хотя функторы, которые мы использовали выше, являются довольно простыми и понятными, но обычно они используются в более продвинутых/сложных темах программирования и заслуживают отдельного урока.
Напишите класс, переменной-членом которого является строка. Перегрузите оператор () для возврата подстроки, которая начинается с индекса, указанного в значении первого параметра. Второй параметр должен указывать требуемую длину подстроки.
Подсказки:
Вы можете использовать индекс массива [] для доступа к отдельным символам строки.
Вы можете использовать оператор += для добавления чего-либо к строке.
Что такое функторы C ++ и их использование?
Я много слышу о функторах в C++. Может ли кто-нибудь дать мне общее представление о том, кто они и в каких случаях они будут полезны?
Есть несколько приятных вещей о функторах. Во-первых, в отличие от обычных функций, они могут содержать состояние. Приведенный выше пример создает функцию, которая добавляет 42 к тому, что вы ей даете. Но это значение 42 не является жестко заданным, оно было указано в качестве аргумента конструктора при создании нашего экземпляра функтора. Я мог бы создать еще один сумматор, который добавил 27, просто вызвав конструктор с другим значением. Это делает их красиво настраиваемыми.
Если бы я вместо этого передал указатель на функцию, компилятор не смог бы сразу увидеть, на какую функцию он указывает, поэтому, если он не выполнит несколько довольно сложных глобальных оптимизаций, ему придется разыменовать указатель во время выполнения, а затем выполнить вызов.
и вы можете использовать boost :: bind для добавления состояния в этот функтор
и самое полезное: с boost :: bind и boost :: function вы можете создать функтор из метода класса, на самом деле это делегат:
Вы можете создать список или вектор функторов
Есть одна проблема со всем этим, сообщения об ошибках компилятора не читаются человеком 🙂
Настоящее преимущество в том, что функтор может удерживать состояние.
Название «функтор» традиционно использовалось в теория категорий задолго до появления C++ на сцене. Это не имеет ничего общего с C++ концепцией функтора. Лучше использовать имя объекта функции вместо того, что мы называем «функтором» в C++. Так другие языки программирования называют подобные конструкции.
Используется вместо простой функции:
Используется вместо указателя функции:
Используется вместо виртуальной функции:
Затем вы можете передать объект MultiplyBy в алгоритм типа std :: transform:
Для новичков, таких как я, среди нас: после небольшого исследования я выяснил, что сделал код, опубликованный Джалфом.
Чтобы создать функтор, сначала вы создаете свой класс. Затем вы создаете конструктор для класса с параметром по вашему выбору типа и имени. В том же операторе следует список инициализатора (в котором используется один оператор двоеточия, что я также недавно знал), который создает объекты-члены класса с ранее объявленным параметром для конструктора. Затем () operator перегружается. Наконец, вы объявляете приватные объекты созданного вами класса или структуры.
Мой код (я нашел, что имена переменных jalf сбивают с толку)
Если что-то из этого является неточным или просто неправильным, не стесняйтесь исправлять меня!
Вот простой пример, который преобразует тип в double :
Вот реальная ситуация, когда я был вынужден использовать Functor для решения моей проблемы:
У меня есть набор функций (скажем, 20 из них), и все они идентичны, за исключением того, что каждая вызывает свою особую функцию в 3 конкретных местах.
Это невероятная трата и дублирование кода. Обычно я просто передаю указатель на функцию и вызываю ее в 3 точках. (Таким образом, код должен появляться только один раз, а не двадцать раз.)
Но потом я понял, что для каждой конкретной функции требуется совершенно другой профиль параметров! Иногда 2 параметра, иногда 5 параметров и т.д.
РЕШЕНИЕ: Итак, я сделал класс-оболочку («Functor»), который может вызывать любые функции, которые мне нужны. Я устанавливаю его заранее (с его параметрами и т.д.), А затем передаю его вместо указателя функции. Теперь вызываемый код может запускать Functor, не зная, что происходит внутри. Он может даже звонить несколько раз (мне нужно было звонить 3 раза)
Они наиболее полезны в ситуациях, когда вам необходимо связать некоторые данные с повторными или отложенными вызовами функции.
Например, связанный список функторов может использоваться для реализации базовой системы синхронных сопрограмм с низким объемом служебных данных, диспетчера задач или прерываемого анализа файлов. Примеры:
Конечно, эти примеры сами по себе не так полезны. Они только показывают, как функторы могут быть полезны, сами функторы очень простые и негибкие, и это делает их менее полезными, чем, например, то, что обеспечивает повышение.
Функторы используются в gtkmm для подключения некоторой кнопки GUI к реальной функции или методу C++.
Таким образом, вы не можете запускать (простым очевидным способом) методы из вашего класса в потоке, не делая ничего лишнего.
Большим преимуществом реализации функций как функторов является то, что они могут поддерживать и повторно использовать состояние между вызовами. Например, многие алгоритмы динамического программирования, такие как алгоритм Вагнера-Фишера для вычисления расстояния Левенштейна между строками, работают, заполняя большую таблицу результатов. Распределять эту таблицу при каждом вызове функции крайне неэффективно, поэтому реализация функции в качестве функтора и превращение таблицы в переменную-член может значительно повысить производительность.
Ниже приведен пример реализации алгоритма Вагнера-Фишера в качестве функтора. Обратите внимание, как таблица размещается в конструкторе, а затем повторно используется в operator() с изменением размера по мере необходимости.
Чтобы добавить, я использовал функциональные объекты для подгонки существующего унаследованного метода к шаблону команды; (единственное место, где красота OO истинной парадигмы OCP я ощутил); Также добавляем сюда шаблон адаптера связанной функции.
Предположим, у вашего метода есть подпись:
Кроме того, нам нужен вспомогательный метод mem_fun3 для вышеуказанного класса, чтобы помочь в вызове.
Теперь, чтобы связать параметры, мы должны написать функцию связывания. Итак, вот оно:
Теперь мы должны использовать это с классом Command; используйте следующую typedef:
Вот как вы это называете:
Примечание: f3 (); вызовет метод task1-> ThreeParameterTask (21,22,23) ;.
Полный контекст этого шаблона в следующем ссылка
Но локальный функтор не может получить доступ к внешним авто переменным. Функция лямбда (C++ 11) является лучшим решением.