Czym są funktory C++ i ich zastosowania?

Ciągle dużo słyszę o funkcjach w C++. Czy ktoś może mi dać przegląd tego, czym są i w jakich przypadkach by się przydały?

15 answers

Funktor jest w zasadzie klasą, która definiuje operator (). Pozwala to na tworzenie obiektów ,które "wyglądają jak" funkcja:

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i
Jest kilka fajnych rzeczy o funkcjach. Jednym z nich jest to, że w przeciwieństwie do zwykłych funkcji, mogą one zawierać stan. Powyższy przykład tworzy funkcję, która dodaje 42 do tego, co mu podasz. Ale ta wartość 42 nie jest zakodowana na twardo, została określona jako argument konstruktora podczas tworzenia naszej instancji functora. Mógłbym stworzyć kolejny adder, który dodał 27, po prostu przez wywołanie konstruktora o innej wartości. Dzięki temu można je łatwo dostosować.

Jak pokazują ostatnie linie, często przekazujesz funktory jako argumenty innym funkcjom, takim jak STD:: transform lub innym standardowym algorytmom biblioteki. Możesz zrobić to samo ze zwykłym wskaźnikiem funkcji, z wyjątkiem, jak powiedziałem powyżej, funktory można "dostosować", ponieważ zawierają stan, co czyni je bardziej elastycznymi (gdybym chciał użyć wskaźnika funkcji, musiałbym napisać funkcję, która dodała dokładnie 1 do swojego argumentu. Funktor jest ogólny i dodaje to, czym go zainicjowałeś), a także jest potencjalnie bardziej wydajny. W powyższym przykładzie kompilator wie dokładnie, którą funkcję std::transform należy wywołać. Powinno wywołać add_x::operator(). Oznacza to, że może on wbudować to wywołanie funkcji. A to sprawia, że jest tak wydajny, jak gdybym ręcznie wywoływał funkcję na każdej wartości wektora.

Gdybym zamiast tego podał wskaźnik funkcji, kompilator nie mógłby od razu zobaczyć, który funkcja, na którą wskazuje, więc jeśli nie wykonuje dość złożonych globalnych optymalizacji, musi dereferować wskaźnik w czasie wykonywania, a następnie wykonać połączenie.

 881
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-19 18:00:54

Mały dodatek. Możesz użyć boost::function, aby utworzyć funktory z funkcji i metod, jak to:

class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"
Aby dodać stan do tego functora, możesz użyć boost::bind]}
boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"

I najbardziej użyteczne, z funkcją boost::bind i boost::można utworzyć functor z metody class, w rzeczywistości jest to delegat:

class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"

Można utworzyć listę lub wektor funktorów

std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));

Jest jeden problem z tymi wszystkimi rzeczami, komunikaty błędów kompilatora nie są czytelne dla człowieka :)

 113
Author: Lazin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-07 11:33:44

Funktor to obiekt, który działa jak funkcja. Zasadniczo klasa, która definiuje operator().

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);
[[3]}rzeczywistą zaletą jest to, że funktor może utrzymać stan.
class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}
 76
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-07 11:34:28

Nazwa "functor" była tradycyjnie używana w category theory na długo przed pojawieniem się C++ na scenie. Nie ma to nic wspólnego z koncepcją C++ functora. Lepiej jest używać nazwy function object zamiast tego, co nazywamy "functorem" w C++. W ten sposób inne języki programowania nazywają podobne konstrukcje.

Używane zamiast funkcji zwykłej:

Funkcje:

  • obiekt funkcji może mieć stan
  • obiekt Function pasuje do OOP (zachowuje się jak co drugi obiekt).

Wady:

  • zwiększa złożoność programu.

Używany zamiast wskaźnika funkcji:

Funkcje:

  • obiekt funkcji często może być inlined

Wady:

  • obiekt Function nie może być zamieniony z innym typem obiektu function podczas pracy runtime (przynajmniej chyba, że rozszerza on jakąś klasę bazową, która w związku z tym daje pewien narzut)

Używany zamiast wirtualnego Funkcja:

Funkcje:

  • Function object (non-virtual) nie wymaga Vtable i wysyłania w trybie runtime, dlatego jest bardziej wydajny w większości przypadków

Wady:

  • obiekt Function nie może być zamieniony z innym typem obiektu function podczas pracy runtime (przynajmniej chyba, że rozszerza on jakąś klasę bazową, która w związku z tym daje pewien narzut)
 38
Author: doc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-13 10:46:57

Podobnie jak inni wspominali, funktor jest obiektem, który działa jak funkcja, tzn. przeciąża operatora wywołania funkcji.

Funktory są powszechnie używane w algorytmach STL. Są użyteczne, ponieważ mogą utrzymywać stan przed i między wywołaniami funkcji, jak zamknięcie w językach funkcyjnych. Na przykład, można zdefiniować MultiplyBy funktor, który mnoży swój argument o określoną wartość:

class MultiplyBy {
private:
    int factor;

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

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

Wtedy można przekazać MultiplyBy obiekt do algorytmu takiego jak std:: transform:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

Kolejną zaletą functora nad wskaźnikiem do funkcji jest to, że wywołanie może być inlinowane w większej liczbie przypadków. Jeśli przekazałeś wskaźnik funkcji do transform, chyba że to wywołanie zostało zainlinowane i kompilator wie, że zawsze przekazujesz mu tę samą funkcję, nie może on wprowadzić wywołania przez wskaźnik.

 34
Author: Matthew Crumley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-08 12:03:00

Dla początkujących, takich jak ja, wśród nas: po kilku badaniach zorientowałem się, co zrobił kod, który opublikował jalf.

Functor to obiekt klasy lub struktury, który może być "wywołany" jak funkcja. Jest to możliwe dzięki przeciążeniu () operator. () operator (Nie wiem, jak się nazywa) może przyjmować dowolną liczbę argumentów. Inne operatory przyjmują tylko dwie wartości, tzn. + operator może przyjmować tylko dwie wartości (po jednej z każdej strony operatora) i zwracać dowolną wartość, dla której został przeciążony. Możesz zmieścić dowolną liczbę argumentów wewnątrz () operator, co daje jej elastyczność.

Aby utworzyć functora najpierw należy utworzyć klasę. Następnie tworzysz konstruktor do klasy z parametrem wybranego typu i nazwy. W tym samym poleceniu następuje lista inicjalizatorów (która używa jednego operatora dwukropka, w czym też byłem nowy), która konstruuje obiekty należące do klasy z wcześniej zadeklarowanym parametrem konstruktora. Następnie () operator jest przeciążony. Nareszcie ty zadeklaruj prywatne obiekty klasy lub struktury, które utworzyłeś.

Mój kod (znalazłem mylące nazwy zmiennych jalfa)

class myFunctor
{ 
    public:
        /* myFunctor is the constructor. parameterVar is the parameter passed to
           the constructor. : is the initializer list operator. myObject is the
           private member object of the myFunctor class. parameterVar is passed
           to the () operator which takes it and adds it to myObject in the
           overloaded () operator function. */
        myFunctor (int parameterVar) : myObject( parameterVar ) {}

        /* the "operator" word is a keyword which indicates this function is an 
           overloaded operator function. The () following this just tells the
           compiler that () is the operator being overloaded. Following that is
           the parameter for the overloaded operator. This parameter is actually
           the argument "parameterVar" passed by the constructor we just wrote.
           The last part of this statement is the overloaded operators body
           which adds the parameter passed to the member object. */
        int operator() (int myArgument) { return myObject + myArgument; }

    private: 
        int myObject; //Our private member object.
}; 

Jeśli cokolwiek z tego jest niedokładne lub po prostu błędne, nie krępuj się mnie poprawić!

 28
Author: Johanne Irish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-22 21:26:14

Funktor jest funkcją wyższego rzędu , która stosuje funkcję do typów parametryzowanych(tj. szablonowych). Jest to uogólnienie mapy funkcji wyższego rzędu. Na przykład, możemy zdefiniować funktor dla std::vector w następujący sposób:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
    std::vector<U> result;
    std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
    return result;
}

Ta funkcja przyjmuje std::vector<T> i zwraca std::vector<U> gdy dana jest funkcja F, która przyjmuje T i zwraca U. Funktor nie musi być definiowany przez typy kontenerów, może być również definiowany dla dowolnego typu szablonowego, w tym std::shared_ptr:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
    if (p == nullptr) return nullptr;
    else return std::shared_ptr<U>(new U(f(*p)));
}

Oto prosty przykład, który zamienia typ na double:

double to_double(int x)
{
    return x;
}

std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);

std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);

Istnieją dwa prawa, których funktorzy powinni przestrzegać. Pierwszy z nich to prawo tożsamości, które mówi, że jeśli dany funktor ma funkcję tożsamościową, powinna ona być taka sama jak zastosowanie funkcji tożsamościowej do typu, tzn. fmap(identity, x) powinna być taka sama jak identity(x):

struct identity_f
{
    template<class T>
    T operator()(T x) const
    {
        return x;
    }
};
identity_f identity = {};

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);

Kolejnym prawem jest prawo składu, które mówi, że jeśli funktor otrzyma skład dwóch funkcje, to powinno być takie samo jak zastosowanie funktora dla pierwszej funkcji, a następnie ponownie dla drugiej funkcji. Zatem fmap(std::bind(f, std::bind(g, _1)), x) powinno być to samo co fmap(f, fmap(g, x)):

double to_double(int x)
{
    return x;
}

struct foo
{
    double x;
};

foo to_foo(double x)
{
    foo r;
    r.x = x;
    return r;
}

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));
 17
Author: Paul Fultz II,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 17:12:35

Oto sytuacja, w której byłem zmuszony użyć Functora do rozwiązania mojego problemu:

Mam zbiór funkcji (powiedzmy, 20 z nich) i wszystkie są identyczne, z tym, że każda wywołuje inną określoną funkcję w 3 konkretnych miejscach.

To niesamowite marnotrawstwo i powielanie kodu. Normalnie chciałbym po prostu przejść w wskaźniku funkcji, i po prostu wywołać to w 3 miejscach. (Kod ten musi wiÄ ™ c pojawiÄ ‡ siÄ ™ tylko raz, zamiast dwudziestu razy.)

Ale wtedy zdałem sobie sprawę, że w każdym przypadek, konkretna funkcja wymagała zupełnie innego profilu parametrów! Czasami 2 parametry, czasami 5 parametrów itp.

Innym rozwiązaniem byłoby posiadanie klasy bazowej, gdzie określona funkcja jest metodą nadpisaną w klasie pochodnej. Ale czy naprawdę chcę zbudować całe to dziedzictwo, tylko po to, żeby przekazać wskaźnik funkcji????

Rozwiązanie: więc zrobiłem klasę wrapper ("Functor"), która jest w stanie wywołać dowolną funkcję, którą potrzebowałem. Ustawiam go z góry (z jego parametrami, itp.), a następnie przekazuję go zamiast wskaźnika funkcji. Teraz wywołany kod może wywołać funktor, nie wiedząc, co dzieje się wewnątrz. Może nawet zadzwonić wiele razy (potrzebowałem go zadzwonić 3 razy.)


To jest to -- praktyczny przykład, w którym funktor okazał się oczywistym i łatwym rozwiązaniem, co pozwoliło mi zredukować powielanie kodu z 20 funkcji do 1.

 8
Author: Fellow Traveler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-26 06:54:56

Oprócz użycia w funkcji callback, funktory C++ mogą również pomóc w dostarczeniu Matlab do klasy matrix . Istnieje przykład .

 2
Author: Shawn Xie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-07 07:03:17

Funktory są używane w gtkmm do podłączenia niektórych przycisków GUI do rzeczywistej funkcji lub metody C++.


Jeśli używasz biblioteki pthread, aby Twoja aplikacja była wielowątkowa, Functory mogą Ci pomóc.
Aby rozpocząć wątek, jednym z argumentów pthread_create(..) jest wskaźnik funkcji, który ma być wykonywany w jego własnym wątku.
Ale jest jedna niedogodność. Ten wskaźnik nie może być wskaźnikiem do metody, chyba że jest to statyczna metoda , lub jeśli podasz klasę , Jak class::method. I jeszcze jedno, interfejs Twojej metody może być tylko:

void* method(void* something)

Więc nie można uruchomić (w prosty oczywisty sposób) metod z twojej klasy w wątku bez robienia czegoś dodatkowego.

Bardzo dobrym sposobem radzenia sobie z wątkami w C++, jest stworzenie własnej klasy Thread. Jeśli chcesz uruchomić metody z klasy MyClass, to co zrobiłem, to przekształcić te metody w klasy pochodne Functor.

Również klasa Thread ma tę metodę: static void* startThread(void* arg)
Wskaźnik do tego metoda zostanie użyta jako argument do wywołania pthread_create(..). A to, co startThread(..) powinno otrzymać w arg, to void* rzucone odniesienie do instancji w stercie dowolnej klasy pochodnej Functor, które zostanie rzucone z powrotem do Functor* po wykonaniu, a następnie wywołane metodą run().

 1
Author: erandros,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-24 18:57:24

Aby dodać, użyłem obiektów funkcyjnych, aby dopasować istniejącą metodę legacy do wzorca poleceń; (tylko miejsce, gdzie piękno paradygmatu oo true OCP czułem ); Również dodanie tutaj powiązanego wzorca adaptera funkcji.

Załóżmy, że twoja metoda ma podpis:

int CTask::ThreeParameterTask(int par1, int par2, int par3)

Zobaczymy, jak dopasujemy go do wzorca polecenia - w tym celu najpierw musisz napisać adapter funkcji member, aby mogła być wywołana jako obiekt funkcji.

UWAGA-To jest brzydkie i może być możesz użyć boost bind helpers itp. ale jeśli nie możesz lub nie chcesz, to jest jeden sposób.

// a template class for converting a member function of the type int        function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
  public:
explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
    :m_Ptr(_Pm) //okay here we store the member function pointer for later use
    {}

//this operator call comes from the bind method
_Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
{
    return ((_P->*m_Ptr)(arg1,arg2,arg3));
}
private:
_Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};

Potrzebujemy również metody pomocniczej mem_fun3 dla powyższej klasy, aby pomóc w wywołaniu.

template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3> mem_fun3 ( _Ret (_Class::*_Pm)          (_arg1,_arg2,_arg3) )
{
  return (mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3>(_Pm));

}

Teraz, aby powiązać parametry, musimy napisać funkcję Bindera. No to zaczyna się:

template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
//This is the constructor that does the binding part
binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
    :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}

 //and this is the function object 
 void operator()() const
 {
        m_fn(m_ptr,m1,m2,m3);//that calls the operator
    }
private:
    _Ptr m_ptr;
    _Func m_fn;
    _arg1 m1; _arg2 m2; _arg3 m3;
};

I, funkcja pomocnicza do użycia klasy binder3-bind3:

//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
    return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}

Teraz musimy użyć tego z klasą Command; użyj następującego typedef:

typedef binder3<mem_fun3_t<int,T,int,int,int> ,T* ,int,int,int> F3;
//and change the signature of the ctor
//just to illustrate the usage with a method signature taking more than one parameter
explicit Command(T* pObj,F3* p_method,long timeout,const char* key,
long priority = PRIO_NORMAL ):
m_objptr(pObj),m_timeout(timeout),m_key(key),m_value(priority),method1(0),method0(0),
method(0)
{
    method3 = p_method;
}

Oto Jak to się nazywa:

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
      &CTask::ThreeParameterTask), task1,2122,23 );

Uwaga: f3(); wywoła metodę task1- > ThreeParameterTask (21,22,23);.

Pełny kontekst tego wzorca pod następującym linkiem

 1
Author: Alex Punnen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-28 06:50:37

Podobnie jak zostało to powtórzone, funktory są klasami, które można traktować jako funkcje(operator przeciążenia ()).

Są najbardziej przydatne w sytuacjach, w których trzeba powiązać niektóre dane z powtarzanymi lub opóźnionymi wywołaniami funkcji.

Na przykład, linked-list of functors może być użyty do implementacji podstawowego synchronicznego systemu koroutine, dyspozytora zadań lub przerywalnego parsowania plików. Przykłady:

/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
    std::string output;
    Functor(const std::string& out): output(out){}
    operator()() const
    {
        std::cout << output << " ";
    }
};

int main(int argc, char **argv)
{
    std::list<Functor> taskQueue;
    taskQueue.push_back(Functor("this"));
    taskQueue.push_back(Functor("is a"));
    taskQueue.push_back(Functor("very simple"));
    taskQueue.push_back(Functor("and poorly used"));
    taskQueue.push_back(Functor("task queue"));
    for(std::list<Functor>::iterator it = taskQueue.begin();
        it != taskQueue.end(); ++it)
    {
        *it();
    }
    return 0;
}

/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
    std::cout << "i = " << i << std::endl;
    std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
    std::cin >> should_increment;
    return 2;
}
void doSensitiveWork()
{
     ++i;
     should_increment = false;
}
class BaseCoroutine
{
public:
    BaseCoroutine(int stat): status(stat), waiting(false){}
    void operator()(){ status = perform(); }
    int getStatus() const { return status; }
protected:
    int status;
    bool waiting;
    virtual int perform() = 0;
    bool await_status(BaseCoroutine& other, int stat, int change)
    {
        if(!waiting)
        {
            waiting = true;
        }
        if(other.getStatus() == stat)
        {
            status = change;
            waiting = false;
        }
        return !waiting;
    }
}

class MyCoroutine1: public BaseCoroutine
{
public:
    MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
    BaseCoroutine& partner;
    virtual int perform()
    {
        if(getStatus() == 1)
            return doSomeWork();
        if(getStatus() == 2)
        {
            if(await_status(partner, 1))
                return 1;
            else if(i == 100)
                return 0;
            else
                return 2;
        }
    }
};

class MyCoroutine2: public BaseCoroutine
{
public:
    MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
    bool& work_signal;
    virtual int perform()
    {
        if(i == 100)
            return 0;
        if(work_signal)
        {
            doSensitiveWork();
            return 2;
        }
        return 1;
    }
};

int main()
{
     std::list<BaseCoroutine* > coroutineList;
     MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
     MyCoroutine1 *printer = new MyCoroutine1(incrementer);

     while(coroutineList.size())
     {
         for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
             it != coroutineList.end(); ++it)
         {
             *it();
             if(*it.getStatus() == 0)
             {
                 coroutineList.erase(it);
             }
         }
     }
     delete printer;
     delete incrementer;
     return 0;
}

Oczywiście te przykłady nie są aż tak przydatne w siebie. Pokazują one jedynie, w jaki sposób funktory mogą być użyteczne, same funktory są bardzo podstawowe i nieelastyczne, co czyni je mniej użytecznymi niż na przykład to, co zapewnia boost.

 1
Author: nfries88,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-08 05:34:56

Dużą zaletą implementacji funkcji jako funktorów jest to, że mogą one utrzymywać i ponownie wykorzystywać Stan między wywołaniami. Na przykład wiele algorytmów programowania dynamicznego, takich jak algorytm Wagnera-Fischera do obliczania odległości Levenshteina między łańcuchami, działa poprzez wypełnienie dużej tabeli wyników. Jest to bardzo nieefektywne przydzielanie tej tabeli za każdym razem, gdy funkcja jest wywoływana, więc implementacja funkcji jako funktora i uczynienie tabeli zmienną członkowską może znacznie popraw wydajność.

Poniżej znajduje się przykład implementacji algorytmu Wagnera-Fischera jako funktora. Zwróć uwagę, jak Tabela jest alokowana w konstruktorze, a następnie ponownie używana w operator(), ze zmianą rozmiaru w razie potrzeby.

#include <string>
#include <vector>
#include <algorithm>

template <typename T>
T min3(const T& a, const T& b, const T& c)
{
   return std::min(std::min(a, b), c);
}

class levenshtein_distance 
{
    mutable std::vector<std::vector<unsigned int> > matrix_;

public:
    explicit levenshtein_distance(size_t initial_size = 8)
        : matrix_(initial_size, std::vector<unsigned int>(initial_size))
    {
    }

    unsigned int operator()(const std::string& s, const std::string& t) const
    {
        const size_t m = s.size();
        const size_t n = t.size();
        // The distance between a string and the empty string is the string's length
        if (m == 0) {
            return n;
        }
        if (n == 0) {
            return m;
        }
        // Size the matrix as necessary
        if (matrix_.size() < m + 1) {
            matrix_.resize(m + 1, matrix_[0]);
        }
        if (matrix_[0].size() < n + 1) {
            for (auto& mat : matrix_) {
                mat.resize(n + 1);
            }
        }
        // The top row and left column are prefixes that can be reached by
        // insertions and deletions alone
        unsigned int i, j;
        for (i = 1;  i <= m; ++i) {
            matrix_[i][0] = i;
        }
        for (j = 1; j <= n; ++j) {
            matrix_[0][j] = j;
        }
        // Fill in the rest of the matrix
        for (j = 1; j <= n; ++j) {
            for (i = 1; i <= m; ++i) {
                unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1;
                matrix_[i][j] =
                    min3(matrix_[i - 1][j] + 1,                 // Deletion
                    matrix_[i][j - 1] + 1,                      // Insertion
                    matrix_[i - 1][j - 1] + substitution_cost); // Substitution
            }
        }
        return matrix_[m][n];
    }
};
 1
Author: Martin Broadhurst,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-16 09:56:58

Funktor może być również użyty do symulacji definiowania funkcji lokalnej w ramach funkcji. Zapoznaj się z pytaniem iInnym .

Ale lokalny funktor nie może uzyskać dostępu do zewnętrznych zmiennych auto. Lepszym rozwiązaniem jest funkcja lambda (C++11).

 0
Author: Shawn Xie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:53

"odkryłem" bardzo ciekawe zastosowanie funktorów: używam ich, gdy nie mam dobrej nazwy dla jednej metody, ponieważ funktor jest metodą bez nazwy; -)

 -9
Author: JChMathae,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-16 13:29:25