Informatik C++ Modern and Lucid C++ for Professional Programmers part 9 Prof. Peter Sommerlad Institutsleiter IFS Institute for Software Rapperswil, HS 2015
Functors and Parameterizing STL Functors, Lambdas, Standard Functors, std::function<>
Function Objects struct donothingfunctor{ We can overload the function call operator as member of a class -> Functor class, instance: functor object ret operator()(params) {... ; void operator()()const{ Two pairs of parentheses required! usually const-member, unless changing member variables of the function object Member variables can keep memory! donothingfunctor{(); construct and call 3
Example: Functor with memory #include <iostream> #include <algorithm> #include <vector> struct averager { void operator()(double d) { accumulator += d; ++counter; double sum() const { return accumulator; double count() const { return counter; double average() const { return sum()/count(); private: double accumulator{; unsigned counter{; ; int main(){ std::vector<double> v{7,4,1,3,5,3.3,4.7; auto const res=for_each(v.begin(),v.end(),averager{); std::cout << "sum = " << res.sum() << '\n' << "count = " << res.count() << '\n' << "average = "<< res.average() << '\n'; res is resulting averager object 4
Function/Functor Terminology Function: unary function, binary function,... Operator function: unary operator, binary operator Functor: unary functor, binary functor Predicate: function/functor with bool result unary predicate: a property of its argument binary predicate: compare two arguments 5
Generator Functors std::vector<int> v; int x{; // memory for lambda below generate_n(std::back_inserter(v),10,[&x]{ ); ++x; return x*x; generate() algorithm requires function with "memory" if not all values should be the same (nullary function, no parameters) Lambda requires variable captured by & or mutable lambda class make_squares{ int x{; public: int operator()() { ++x; return x*x; ; //... generate(v.begin(),v.end(),make_squares{); 6
Lambda Function Syntax Defining Inline Functions definition auto const g=[](char c)->char{return std::toupper(c);; g('a'); call auto const for function variable from Lambda is used to call function [] introduces Lambda function can contain captures, e.g., [=] or [&] to access variables (parameters), as with other functions, but can be auto -> trailing return type, if cannot be deduced (not needed) body block with statements 7
Recap Lambda Rules [capture](parameters)mutableopt->return_type{body Capture: [=], [&], [var=value] Parameters: auto p1, int p2, auto mutable: allow changing capture variables in body Return_type: typically deduced automatically Body: return statement(s) provide return type implicitly 8
Lambda Captures [capture](parameters)mutableopt->return_type{body Capture: [=] - default implicit capture variables used in body by value [&] - default capture variable used in body by reference [var=value] - introduce new capture variable with value combinations of specific captures with a default [=,&out] - capture all by copy, but out by reference [&,=x] - capture all by reference, but x by copy/value Guideline: always capture variables explicitly 9
Lambda Special Case: mutable generate_n(std::back_inserter(v),10,[x=0]() mutable { return ++x, x*x; // mutable allows change allow changing x ); introduce new capture variable x variables captured by copy (=) are const within the lambda, unless... the lambda is marked mutable, and the lambda gets its own copy of the variable, or the lambda defines its capture with an initializer lambdas are mapped internally to functors 10
Lambdas are functors A C++ compiler internally maps lambdas to functor objects with an unaccessible/hidden class name [&x]{ ++x; return x*x; struct _unknown_generator_name_{ _unknown_generator_name_(int &x):x{x{ auto operator()() const { ++x; return x*x; private: int &x; ; [](char c)->char{ return std::toupper(c); struct _unknown_converter_name_ { auto operator()(char c) const ->char{ return std::toupper(c); ; 11
Lambdas in Member Functions this-> struct DemoLambdaMemberVariables { int x{; std::vector<int> demoaccessingmemberfromlambda() { std::vector<int> v; generate_n(back_inserter(v),10,[=] { return ++x, x*x; // member x can be changed ); return v; ; capture this by copy Captured member variables are always captured by reference, even if the default is [=] Reason: this is a pointer (reference) and if copied, member variables are referred from it this can not be captured by reference 12
Standard Functor template Classes transform(v.begin(),v.end(),v.begin(),[](int x){return -x;); transform(v.begin(),v.end(),v.begin(),std::negate<int>{); Lambdas make applying transform etc. quite easy However, if there is only a simple expression used, there is a chance for reuse of the standard library's functor classes for almost all operators also relational operators, e.g., sorting in descending order sort(v.begin(),v.end(),std::greater<>{); parameter type deduced 13
standard functor classes #include <functional> binary arithmetic and logical plus<> (+) minus<> (-) divides<> (/) multiplies<> (*) modulus<> (%) logical_and<> (&&) logical_or<> ( ) transform(v.begin(),v.end(),v.begin(), v.begin(),std::multiplies<>{); unary negate<> (-) logical_not<> (!) binary comparison less<> (<) less_equal<> (<=) equal_to<> (==) greater_equal<> (>=) greater<> (>) not_equal_to<> (!=) 14
Parameterize Associative Containers std::set<int,std::greater<>> reverse_int_set{; Associative containers allow an additional template argument for the comparison operation it must be a functor class giving a binary predicate predicate must be irreflexive and transitive Own functors can provide special sort order, e.g., case-less comparison of strings caution: requirements for sorting must be fulfilled, e.g. >= (std::greater_equal) is not allowed (is reflexive!) 15
Example: set<string> for dictionary #include <set> #include <functional> #include <algorithm> #include <cctype> #include <iterator> #include <iostream> struct caseless{ using string=std::string; bool operator()(string const &l, string const &r){ return std::lexicographical_compare( l.begin(),l.end(),r.begin(),r.end(), [](char l,char r){ return std::tolower(l) < std::tolower(r); ); ; int main(){ using std::string; using caseless_set=std::multiset<string,caseless>; using in=std::istream_iterator<string>; caseless_set wlist{in{std::cin,in{; std::ostream_iterator<string> out{std::cout,"\n"; copy(wlist.begin(),wlist.end(),out); binary predicate strings as ranges lambda as binary predicate on char pass predicate functor as template argument 16
How to keep functors around? In situations where you not only provide a functor but want to take a function or functor as parameter, e.g., in a constructor and later apply it, what should be the type of the (member) variable and the parameter? foo(double f(double)); doesn't work with functors neither does your specific functor type for others 2 options: template typename parameter (later) std::function<double(double)> 17
std::function<signature> std::function<bool(int)> apredicate{; Universal function holder can store functions, functor instances or lambdas signature must be compatible with the SIGNATURE function template argument can be reassigned or emptied can be empty -> might need to check if (apredicate) { // implicit bool conversion: is valid? cout << "pred(42) is " << apredicate(42) << '\n'; else { cout << "empty function\n"; 18
#include <functional> #include <iostream> Example: using std::function<> void apply_and_print(std::ostream& cout, std::function<bool(int)> apredicate) { if (apredicate) { // implicit bool conversion cout << "pred(42) is " << apredicate(42) << '\n'; else { cout << "empty function holder\n"; function parameter check for validity call operator() of int main(){ using std::cout; cout << std::boolalpha; // true/false in output std::function<bool(int)> apredicate{; apply_and_print(cout,apredicate); apredicate = [](int i){return i%2;; apply_and_print(cout,apredicate); apredicate = std::not1(apredicate); apply_and_print(cout,apredicate); apredicate = nullptr; apply_and_print(cout,apredicate); default is empty -> invalid to call std::not1() creates logically negated unary predicate functor nullptr assignment makes function empty 19
std::function with member functions #include <functional> #include <iostream> member function to wrap struct X { int calc(int i) const { return x*i; private: int x{7; ; make variable const to avoid invalid function objects (general rule!) int main(){ std::function<int (X const &,int)> const f{&x::calc; X const anx{; std::cout << f(anx,6); must pass object as this argument explicitly member function pointer must specify & address-of operator to member 20
What is a member-(function) pointer? It is possible to refer to individual class members using "pointer-to-members" e.g., to handle different members of the same type by the same function syntax of the type: type Class::* varname; initialize such a value by: &Class::member This extends to "pointer-to-member-functions" syntax: resulttype (Class::*)(params) 21
Example Pointer to Members.* struct X { void foo() const { std::cout << a << " foo\n"; void bar() const { std::cout << b << " bar\n"; int a; int b; ; must match m-f signature including const and parameters void doit(void (X::*mfunc)() const, X const &x){ (x.*mfunc)(); // parenthesis needed void change(int X::*memvar, X& x, int val){ use operator.* for member function pointer () required - precedence x.*memvar = val; use operator.* for pointer to member access int main(){ X x{1,2; doit(&x::foo,x); doit(&x::bar,x); change(&x::a,x,42); change(&x::b,x,43); doit(&x::foo,x); doit(&x::bar,x); 1 foo 2 bar 42 foo 43 bar 22
std::function calling member function auto g=std::function<void(x const&, int)>{&x::foo; X x{42,43; g(x,1); // calls -> x.foo(1) x.bar(2); struct X { void foo(int) const ; void bar(int) const ; int a; int b; ; std::function for a member function makes passing the this object explicit Here you see, that what is left of the. operator is actually passed to the member function as an argument 23
Creating your own iterator types #include <iterator> algorithms are parameterized by functors but also by iterators You can create or wrap iterators using boost::iterators library #include <boost/iterator/counting_iterator.hpp> #include <boost/iterator/filter_iterator.hpp> #include <boost/iterator/transform_iterator.hpp> Or you can implement your own iterators (see C++ advanced) with the help of <boost/operators.hpp> 24
Recap: Iterator Categories Different algorithms require different strengths of iterators InputIterator - read sequence once OutputIterator - write results, without designating an end ForwardIterator - read/write sequence, multiple passes BidirectionalIterator - read/write sequence, back-forth RandomAccessIterator - read/write/indexed sequence more versatile iterator can be used for more efficient algorithm Iterator's capabilities can be determined at compile time 25
Boost Iterator Library #include <boost/iterator/counting_iterator.hpp> #include <boost/iterator/filter_iterator.hpp> #include <boost/iterator/transform_iterator.hpp> Several pre-defined adapters with factory functions, for example counting filtering transforming See also http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/ 26
Example: using Boost Iterator struct odd{ bool operator()(int n)const{return n%2; ; int main(){ using counter=boost::counting_iterator<int>; std::vector<int> v(counter{1,counter{11); std::ostream_iterator<int> out{std::cout,", "; copy(v.begin(),v.end(),out); std::cout << '\n'; using boost::make_filter_iterator; copy(make_filter_iterator(odd{,v.begin(),v.end()), make_filter_iterator(odd{,v.end(),v.end()), out); std::cout << '\n'; using boost::make_transform_iterator; auto square=[](auto x){ return x*x;; copy(make_transform_iterator(v.begin(),square), make_transform_iterator(v.end(),square), out); functor for filtering counting iterator filter iterator, only odd values provided transform iterator applies function/functor/ lambda for each value 27
Summary Parameterize the standard library through Functors Lambdas Iterators construct your own with boost::iterator library Store and pass functions / function objects in std::function<> 28