Funkcje, predykaty i obiekty funkcyjne¶
Funkcje¶
Wiele algorytmów standardowych wymaga przekazania adresu funkcji jako argumentu.
void print_element(int value)
{
std::cout << "Item: " << value << '\n';
}
std::list<int> lst = { 1, 2, 3, 4, 5 };
std::for_each(lst.begin(), lst.end(), &print_element);
Predykaty¶
Predykat - funkcja, która zwraca wartość logiczną bool (true/false
) lub int
bool is_leap_year(int year)
{
if ( 0 == year % 400 ) return true;
if ( 0 == year % 100 ) return false;
if ( 0 == year % 4 ) return true;
return false;
}
auto first_leap =
std::find_if(lst.begin(), lst.end(), &is_leap_year);
Obiekt funkcyjny - funktor¶
- Obiekt będący instancją klasy definiującej operator() (wywołania funkcji)
- Może być wywołany za pomocą tej samej składni co funkcja
class BiggerThan
{
const int test_value_;
public:
BiggerThan (int x) : test_value_{x} {}
bool operator () (int val) const
{
return val > test_value_;
}
};
auto pos = std::find_if(lst.begin(), lst.end(), BiggerThan(12) );
Funktory arytmetyczne i logiczne¶
- Funktory arytmetyczne i logiczne
- proste szablony klas, przeciążające operator
( )
, tak żeby wykonywały one operację logiczną lub arytmetyczną.
Dwuargumentowe funktory:
- arytmetyczne
- divides (
/
) - minus (
-
) - modulus (
%
) - multiplies (
*
) - plus (
+
)
- divides (
- logiczne
- logical_and (
&&
) - logical_or (
||
)
- logical_and (
- bitowe
- bit_and (
x & y
) - bit_or (
x | y
) - bit_xor (
x ^ y
)
- bit_and (
vector<int> data1 = { 1, 2, 3 };
vector<int> data2 = { 3, 2, 1 };
vector<int> sum(data1.size());
transform(data1.begin(), data1.end(), data2.begin(), sum.begin(), plus<int>{});
transform(data1.begin(), data1.end(), data2.begin(), sum.begin(), plus<>{}); // C++14
Jednoargumentowe funktory:
- negate (
-
) - logical_not (
!
) - bit_not (
~
)
Funktory porównujące¶
Funktory porównujące - proste szablony klas, których przeciążony operator ( )
wykonuje porównanie relacji lub równości.
- equal_to (
==
) - greater (
>
) - greater_equal (
>=
) - less (
<
) - less_equal (
<=
) - not_equal_to (
!=
)
vector<int> vec = { 5, 21, 3, 1, 8, 9 };
sort(vec.begin(), vec.end(), greater<int>{});
Wrapper - std::mem_fn¶
std::mem_fn
umożliwia utworzenie obiektu funkcyjnego, który
przechowuje wskaźnik do metody klasy. Przekazanie
referencji, wskaźnika lub smart-pointer’a do obiektu jako argumentu wywołania powoduje
wywołanie odpowiedniej metody dla przekazanego obiektu.
class Person
{
std::string name_;
public:
explicit Person(const std::string& name) : name_{name}
{}
void print() const
{
cout << "Person: " << name_ << endl;
}
};
Person p{ "John" };
auto name_printer = std::mem_fn(&Person::print);
name_printer(p); //
name_printer(&p);
auto ptr_p = std::make_shared<Person>("Adam");
name_printer(ptr_p);
Funktor mem_fn
jest często wykorzystywany w algorytmach STL:
std::vector<Person> stuff = { Person{"John"}, Person{"Adam"}, Person{"Maria"} };
std::for_each(stuff.begin(), stuff.end(), std::mem_fn(&Person::print);
Wyrażenia lambda¶
Wyrażenie lambda to definicja „w miejscu” obiektu funkcyjnego, który można następnie użyć jako zmienną lub argument wywołania funkcji (np. algorytmu standardowego).
Minimalne wyrażenie lambda:
[] { std::cout << "hello world" << std::endl; }();
wyrażenia lambda mogą przyjmować parametry, a także zwracać wartość:
auto l = [] (int x, int y) {
return x + y;
};
int result = l(2,3) // 5
Wewnątrz nawiasów klamrowych możemy zawrzeć elementy, które lambda ma przechwycić z zakresu w którym jest tworzona.
[=]
zakres (scope) jest przekazany przez wartość.- Wewnątrz lambdy możemy odczytać wartość zmiennych zewnętrznych taką, jaka była w momencie tworzenia lambdy (domknięcie).
[&]
zakres (scope) jest przekazany przez referencję.- Lambda ma dostęp do odczytu i zapisu zmiennych z zakresu w którym została utworzona.
vector<int> vec = { 1, 2, 3, 4, 5 };
const int value = 5;
auto add5 = [=](int x) {
return x + value;
};
transform(vec.begin(), vec.end(), vec.begin(), add5);
int sum{};
for_each(vec.begin(), vec.end(), [&sum](int x) { sum += x; });
Typ domknięcia dla wyrażenia lambda określić używając słowa auto
:
auto l = [] (int x, int y) {
return x + y;
};
Funkcje mogą zwracać lambdy. Wymagane jest wówczas określenie typu zwracanego jako:
std::function
- w C++11std::function<int()> create_generator(size_t seed) { return [seed]() mutable { return ++seed; }; }
auto
w C++14auto create_generator(size_t seed) { return [seed]() mutable { return ++seed; }; }
Użycie funkji create_generator()
:
vector<int> vec(10);
generate_n(vec.begin(), 10, create_generator(42));