To znowu ja :D
Na początku powiedzmy sobie, że nazwa funkcji jest wskaźnikiem na nią. Możemy w taki sposób przekazać funkcje do innej funkcji tak jak widać na przykładzie pierwszej instancji template-a funkcji p.
Druga sprawa - klasa O ma zdefiniowany operator(), więc możemy jej obiektów używać jak funkcji (tzn. z poziomu kodu tak to wygląda). Takie klasy nazywane są funktorami btw.
Tak więc w pierwszym przypadku:
Do p przekazujemy wskaźnik na int wskazujący na 0 element tablicy i jako drugi parametr wskaźnik wskazujący (4 * sizeof(int)) komórek* dalej (na czwarty, ostatni element). Przekazujemy też funkcje f o czym wcześniej mówiłem. Wewnątrz funkcji p mamy prostą pętle. Został wykorzystany do...while tak więc pętla się zatrzyma zaraz po obejściu w którym i == e. Pętla każdy obiekt (tutaj int), między zakresem ustalonym wskaźnikami (w 1 i 2 parametrze funkcji), przekazuje do funkcji f. A że funkcja f przyjmuje referencje, to działa na oryginalnej zmiennej i do każdego inta dodaje 2. Także za 40. linią każdy element tablicy t ma wartość o 2 większą.
W drugim przypadku dokładnie to samo, tylko zamiast funkcji f korzystamy z O::operator() wywoływanego z obiektu o (a właściwie jego kopii, bo funkcja p (ta instancja, bo to szablon ofc) nie przyjmuje przez referencje i skopiuje sobie obiekt, ale nie obchodzi nas to specjalnie w tym przypadku). Skoro przy tworzeniu o podaliśmy do konstruktora -1, to O::operator() od każdego elementu tablicy odejmie -1 (czyli doda 1) - bo też przyjmuje przez referencje.
Jeszcze jedno - tutaj nie korzystaliśmy ze zwykłych wskaźników, a z iteratorów, ale one też mają zdefiniowany operator++ i !=, więc nie ma problemu. Działają na tej samej zasadzie. Iteratory możesz traktować jak wskaźniki ale wewnątrz-kontenerowe (wskazują na elementy konkretnej klasy kontenera).
Nie musieliśmy sami definiować parametrów szablonu, bo zostały automatycznie ustalone przez kompilator na podstawie przekazywanych typów.
* - miałem na myśli komórki pamięci, nie tablicy. Dla tablicy to oczywiście są 4 komórki.
Btw.: w kodzie jest "mały" błąd. W przypadku intów crash nie jest pewny, ale gdyby to był vector jakichś obiektów i funkcja p próbowałaby dostać się do jakichś składowych obiektu, to crash gwarantowany. Konkretnie w drugim przypadku w ostatniej iteracji O::operator() dodaje do *(v.end()), a .end() zwraca iterator na "element za ostatnim elementem" (nieistniejący)...