Funktory w C++

0

Czy ktoś mógłby podrzucić jakiś najprostszy przykład w którym będzie pokazane dlaczego powinno używać się funktorów, zamiast wywoływania "normalnych" funkcji ?

Coś na prawdę na podstawowym poziomie tak by zrozumieć idee przechowywania danych przez funktory, czegoś czego nie potrafią zrobić funkcje - nie za bardzo w ogóle rozumiem tą różnicę, więc chętnie zobaczyłbym to na przykładzie z jakimś objaśnieniem.

dzięki !

4

Funktorow sie uzywa (uzywalo glownie) dlatego, ze mozna przekazac dodatkowy 'stan' do funkcji, nie za posrednictwem argumentow (a sygnatura najczesciej jest wymagana przez funkcje do ktorej przekazujemy funktor).

Na przyklad majac taka funkcje:

template<typename T, typename Predicate>
vector<T> filter(vector<T> src, Predicate p) {
  vector<T> res;
  for(auto const& elem : src) if(p(elem)) res.push_back(elem);
  return res;
}

Potrzebujemy przekazac predykat przyjmujacy jeden argument. Zakladajac na chwile, ze nie mamy lambd, w przypadku takiego vectora:

vector<int> v { 1, 2, 3 };

i checi pobrania wszystkich wartosci wiekszych od N, musielibysmy trzymac jakis rodzaj globalnej zmiennej przechowujacej to N.

Zamiast tego mozemy sobie zdefiniowac funktor

struct GreaterThan {
  GreaterThan(int n): n(n) {;}

  bool operator()(int elem) {
    return elem > n;
  }
private:
  int n;
};

W C++11 doszly lambdy, wiec i potrzeba na funktory zmalaly, ale wciaz sa wykorzystywane w ten sposob, jesli cialo funkcji jest duze/funkcja ma byc czesto wykorzystywana, lub jest rodzajem api udostepnianego przez jakas biblioteke.

Edit: Ah, jeszcze mozna uzywac funkcji pokroju bind, ale one sa mniej elastyczne od funktorow (np. pojedyncza funkcja nie ma jak zhermetyzowac funkcji pomocniczych).

0

Mam jeszcze takiej pytanie, powiedzmy że mamy jakąś funkcje z biblioteki algorithm. Dajmy na to std::partition, która sortujemy elementy względem dowolnego predykatu, spełniające ten predykat wyrzuca na początek, nie spełniające na koniec.

Mamy sobie takowy kod:

#include <iostream> 
#include <algorithm> 
#include <vector> 

using namespace std; 
//** PREDYKAT WERSJA PIERWSZA  **// 

bool pred(int x) { int piwot = 5; return x<piwot; } 

//** PREDYKAT WERSJA DRUGA ** //

int piwot = 5; 

bool pred(int x) { return x<piwot; } 

//**DALSZA CZĘŚĆ KODU**// 

int main(void)
{
   vector<int>myVec; 

   for(int i=0; i<10; ++i)
      myVec.push_back(i); 

   vector<int>::iterator granica; 

   granica = partition(myVec.begin(), myVec.end(), pred); 

   for(vector<int>::iterator it = myVec.begin(); it != bound; ++it) 
      cout << *it << " "; 
      cout << endl; 

   for(vector<int>::iterator it = bound; it != myVec.end(); ++it) 
      cout << *it << " "; 
      cout << endl; 

}

 

W pierwszej wersji predykatu, wartość zmiennej piwot według której będzie sortowany ciąg (dzielony na elementy mniejsze czy większe) jest ustalona przed zmienną zadeklarowaną wewnątrz funkcji bool.

W drugiej wersji, wartość zmiennej piwot stanowi wartość zmiennej globalnej.

Trzecia wersja zakłada korzystanie w tym wypadku z obiektu funkcyjnego, który będzie przechowywał wartość zmiennej w polu prywatnym (klasa z przeciążonym operatorem wywołania).

Pytanie: dlaczego w tym wypadku rekomenduje się używanie obiektu funkcyjnego, a nie korzystanie ze zmiennej globalnej lub zadeklarowanej w funkcji bool ?

Na prawdę jest jakaś różnica, jakieś niebezpieczeństwo podczas pisania programu że nagle użyjemy tej zmiennej globalnej do czegoś innego. Nie potrafię zrozumieć sensu wykorzystania obiektu fukcyjnego w tak błahym przypadku, gdzie najzwyczajniej w świecie moglibyśmy posłużyć się standardowym rozwiązaniem wykorzystującym zmienną globalną...

1

Zmienna globalna jest jedna. Co zrobisz jeśli będziesz chciał sobie posortować dwa różne kontenery według różnych predykatów (np. ≥5 i ≥3)? Utworzysz kolejną zmienną globalną?

3

@os_x
Kazdy globalny stan, z ktorego korzystasz utrudnia:
#Testowanie
#Debuggowanie
#Rozszerzanie
#Utrzymanie
#Pisanie
kodu. Jestes pewien czy to jest warte tych paru linijek wiecej?

0

@kq, posłużysz się jakimś przykładem żeby to zoobrazować, nie wiem w jaki sposób obiekt funkcyjny może przechowywać różne wartości. Jakiś krótki przykład odnośnie mojego prostego problemu byłby pomocny. Bo tak za bardzo tego nie widze.

3

Może przechowywać różne wartości w różnych instancjach. Zmienna globalna ma z definicji jedną instancję.

0

No zgoda, tylko nie rozumiem dlaczego w takiej sytuacji nie mogę ot tak po prostu zmienić wartości tej zmiennej albo dać nową zmienną. Dla mnie to nie problem, może za mało mam doświadczenia by poczuć różnicę.

2

@os_x, jak tylko w twoim programie zrobisz dwa równolegle wątki które sortują coś niezależnie - twoje rozwiązanie leży i kwiczy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1