Функтор — какая у них власть над другими классами, которые не реализовали оператор ( )

Я пытаюсь понять «силу» функторов.

Итак, они являются указателями на функции, но что они могут делать такого, чего не могут другие классы, не реализующие operator()?

Например :

#include <iostream>
#include <assert.h>
#include <vector>
using namespace std;


class MultiplyBy
{
    private:
        int factor;

    public:
        MultiplyBy(int x) : factor(x) {}

        int operator () (int other) const
        {
            return factor * other;
        }
};


int main()
{

    MultiplyBy temp(5);
    int myResult = temp(5);  // now myResult holds 25
    cout << myResult << endl;
    return 0;
}

И мы берем его другого друга

class MultiplyOther
{
    private:
        int factor;
    public:
        MultiplyOther(int x) : factor(x) {}
        int invokeMultiplyMe(int _other)
        {
            return _other * factor;
        }
};

И сделайте то же самое:

int main()
{
    // MultiplyOther

    MultiplyOther notFunctor(4);
    int myOther = notFunctor.invokeMultiplyMe(3);
    cout << myOther << endl;
    return 0;
}

И мы получаем:

25
12

Тогда какова реальная мощность функтора? Оба класса сохраняют состояние, или я что-то здесь упускаю?


person JAN    schedule 26.08.2012    source источник
comment
истинная сила скрывается в лямбдах, я думаю   -  person Mr.Anubis    schedule 26.08.2012
comment
@Mr.Anubis: Lambdas - это синтаксический сахар для функторов, у них больше нет силы.   -  person interjay    schedule 26.08.2012
comment
@interjay вы можете определить их в точке вызова, захватить переменные и т. д. Можете ли вы сделать это с помощью простых функторов, не передавая их в качестве аргументов и т. д.?   -  person Mr.Anubis    schedule 26.08.2012
comment
@Mr.Anubis: Вы можете определить локальный класс прямо перед вызовом. Захваты переменных в лямбда-выражениях — это синтаксический сахар для их хранения в членах класса. Все, что можно сделать с помощью лямбды, можно сделать и с помощью функтора (хотя это может быть гораздо более многословно). Обратное неверно.   -  person interjay    schedule 26.08.2012


Ответы (3)


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

В вашем примере экземпляр MultiplyBy может использоваться любым кодом, который ожидает вызова чего-то, что принимает int и возвращает его (или типы, которые можно построить из /convertible в int). Использование вашего второго примера потребует от кода знания метода invokeMultiplyByMe.

Обратите внимание, что функторы вообще не являются указателями на функции, как указано в вопросе. Это классы, экземпляры которых можно вызывать с синтаксисом вызова функции.

person juanchopanza    schedule 26.08.2012
comment
Ага! "Using your second example would require the code to have knowledge of the invokeMultiplyByMe method" Не ожидал, +1! - person JAN; 26.08.2012
comment
@ron Кстати, я только что увидел ... это указатели на функции ... в вашем вопросе. Это неправда. - person juanchopanza; 26.08.2012
comment
Также интересно: обычно функторы могут быть агрессивно встроены, в результате чего такой код, как for_each(start, end, functor), обычно выполняется быстрее, чем for_each(start, end, func_ptr) - person bstamour; 27.08.2012

Функторы не имеют особых свойств над классами, но их можно использовать как функции с более высокими возможностями. Обычно у функции не может быть состояния, а если оно есть (с помощью статической переменной), то состояние разделяется между всеми вызовами функций.\

Функтор обладает всеми свойствами класса (особенно состоянием), но может использоваться как функция. Отличным примером является функтор, используемый для алгоритмов STL:

class Add
{
  public:
    Add(){sum = 0;};
    void operator()(int i)
    {
        sum += i;
    }
    int getSum(){return sum;};

    private:
    int sum;
}

int main()
{

std::vector<int> vint;
vint.push_back(2);
vint.push_back(3);
vint.push_back(4);
}

Add adder;

adder = std::for_each( vint.begin(); vint.end(), adder);

cout << adder.getSum();
person Sam    schedule 26.08.2012

«так что они являются указателями на функции» - указатели на функции и функторы совершенно разные. Бывший — это просто указатель на исходную функцию. Он не содержит никакого состояния. Однако функтор или функциональный объект содержат состояние. Функтор — это любой класс, который определяет оператор(). Таким образом, любой объект такого класса может быть вызван как функция.

Фрагмент кода из учебника по C++...

template<class T>
class TooBig
{
    private:
    T cutoff;
    public:
    TooBig(const T & t) : cutoff(t) {}
    bool operator()(const T & v) { return v > cutoff; }
};

С помощью этого класса мы можем создать несколько объектов с разными значениями отсечки. Слишком большая отсечка1(10); Слишком большая отсечка2(20);

Одно из лучших мест для использования функтора — это шаблоны. Например, возьмите шаблон списка. У него есть функция remove_if, которая удаляет все значения в списке, которые возвращают true из аргумента.

список список1; // Допустим, у него есть какие-то значения list list2; // допустим, у него есть какие-то значения

теперь вызов list1.remove_if(cutoff1); // применяет все элементы list1 и удаляет те, которые больше значения, хранящегося в cutOff1. Аналогичная вещь для list2.

Спасибо!

person rahul    schedule 26.08.2012