STL Functors: Functions vs. Function Objects
E N D
Presentation Transcript
STL Functors: Functions vs. Function Objects • STL Functors support function call syntax • Algorithms invoke functions and operators • E.g., calling operator< during a sort algorithm • Those expressions define type requirements • In many cases, can plug in alternative behaviors • The STL allows diverse functor types • A function pointer (from Lippman, LaJoie, Moo) bool (*PF) (const string &, const string &); // function pointer bool pf (const string &, const string &); // function • Instance of a class/struct providing operator() • Either one can be invoked (called) • But may have to deal with signature (and return type)
STL Functors Extend STL Algorithms • Make the algorithms/containers even more general • Can be used parameterize policy • E.g., the order produced by a sorting algorithm • E.g., the order maintained by an associative container • Each functor does a single, specific operation • Often implemented as small functions or classes/structs • E.g., a struct with one public member function, operator() • Function objects may also have member variables • Arguments not stored may be supplied at point of call • Member variables can parameterize the operation • E.g., the value k for a functor that adds k to another value • E.g., arguments for an invocation on a remote object
Function Object Use in an Algorithm struct GT_magnitude : public binary_function<double, double, bool> { bool operator() (double x, double y) { return fabs(y) < fabs(x); } }; struct LT_magnitude : public binary_function<double, double, bool> { bool operator() (double x, double y) { return fabs(x) < fabs(y); } }; int main (int, char **) { vector<double> u,v; for (double d = 0.0; d < 10.1; d += 1.0){ u.push_back (d); v.push_back (d); } sort (u.begin(), u.end(), GT_magnitude()); sort (v.begin(), v.end(), LT_magnitude()); ostream_iterator<double> o (cout, “ ”); copy (u.begin(), u.end(), o); copy (v.begin(), v.end(), o); return 0; }
Function Use in an Algorithm #include <iostream> #include <vector> #include <string> #include <iterator> #include <algorithm> using namespace std; struct Employee { Employee (const char * n, int i) : name_(n), id_(i) {} string name_; int id_; }; typedef Employee * EmployeePtr; ostream& operator<< (ostream & os, const EmployeePtr & e) { os << e->name_ << " " << e->id_ << " "; return os; } // function for comparing EmployeePtrs bool id_compare (const EmployeePtr & e, const EmployeePtr & f) { return e->id_< f->id_|| (e->id_ == f->id_ && e->name_ < f->name_); } int main (int, char *[]) { vector<EmployeePtr> v; v.push_back(new Employee("Claire", 23451)); v.push_back(new Employee("Bob", 12345)); v.push_back(new Employee("Alice", 54321)); cout << "v: " ; copy (v.begin(), v.end(), ostream_iterator<EmployeePtr>(cout)); cout << endl; // "v: Claire 23451 Bob 12345 Alice 54321 " sort (v.begin(), v.end(), id_compare); cout << "v: " ; copy (v.begin(), v.end(), ostream_iterator<EmployeePtr>(cout)); cout << endl; // "v: Bob 12345 Claire 23451 Alice 54321 " // clean up: pointers "own" the heap objects for (vector<EmployeePtr>::iterator i = v.begin(); i != v.end(); ++i) { delete *i; } return 0; } heap object function name ok here
Function Object Use in a Container #include <set> #include <string> #include <iterator> #include <algorithm> using namespace std; struct Employee { Employee (const char * n, int i) : name_(n), id_(i) {} string name_; int id_; }; ostream& operator<< (ostream & os, const Employee & e) { os << e.name_ << " " << e.id_ << “ "; return os; } // set needs this (orders by name then id) bool operator< (const Employee & e, const Employee & f) { return e.name_ < f.name_ || (e.name_ == f.name_ && e.id_ < f.id_); } // orders by id then name) struct EmployeeIdComp { bool operator() (const Employee & e, const Employee & f) { return e.id_ < f.id_ || (e.id_ == f.id_ && e.name_ < f.name_); } }; int main (int, char *[]) { vector<Employee> v; v.push_back(Employee("Claire", 23451)); v.push_back(Employee("Bob", 12345)); v.push_back(Employee("Alice", 54321)); cout << "v: " ; copy (v.begin(), v.end(), ostream_iterator<Employee>(cout)); // "v: Claire 23451 Bob 12345 Alice 54321 " set<Employee> s; s.insert(v.begin(), v.end()); cout << "s: " ; copy (s.begin(), s.end(), ostream_iterator<Employee>(cout)); // "s: Alice 54321 Bob 12345 Claire 23451 “ set<Employee, EmployeeIdComp> t; t.insert(v.begin(), v.end()); cout << "t: " ; copy (t.begin(), t.end(), ostream_iterator<Employee>(cout)); // "t: Bob 12345 Claire 23451 Alice 54321 “ return 0; } temporary object function object needed
STL <functional> Before and After C++11 • Parts were deprecated in C++11, but are still available in Visual C++ 2010 • E.g., inherit from unary_function and binary_function to decorate a callable object with associated types reflecting its function call signature • E.g., make a functor for a member function call through a pointer using mem_fun • E.g., make a functor for a member function call through a reference using mem_fun_ref • E.g., bind first argument using binder1st<BinaryFun> • E.g., bind second argument using binder2nd<BinaryFun> • See http://www.sgi.com/tech/stl/table_of_contents.html for more details • New versions introduced in C++11, are also available in Visual C++ 2010 • E.g., bind any argument using bind and _1 _2 _3 etc. • E.g., make a functor for a member function call through pointer or reference using mem_fn • E.g., wrap a callable object with a function call signature using function • New versions provide similar capabilities, but are more consistent and general than before • Availability may depend on compiler version, older code may involve previous versions • See http://en.cppreference.com/w/cpp/utility/functional for more details
Pre-C++11 Member Function Adaptor Example // Based on the SGI C++ STL page examples struct B { virtual void print() = 0; }; struct D1 : public B {void print() {cout<<"I'm a D1"<< endl;}}; struct D2 : public B {void print() {cout<<"I'm a D2"<< endl;}}; int main(int, char **) { D1 d1; D2 d2; vector<B*> v; vector<D2> w; v.push_back(&d1); v.push_back(&d2); v.push_back(&d2); v.push_back(&d1); w.push_back(d2); for_each(v.begin(), v.end(), mem_fun(& B::print)); cout<< endl << endl; for_each(w.begin(), w.end(), mem_fun_ref(& B::print)); return 0; } different wrappers needed
C++11 Member Function Adaptor Example // Previous example modified to use mem_fn in both cases struct B { virtual void print() = 0; }; struct D1 : public B {void print() {cout<<"I'm a D1"<< endl;}}; struct D2 : public B {void print() {cout<<"I'm a D2"<< endl;}}; int main(int, char **) { D1 d1; D2 d2; vector<B*> v; vector<D2> w; v.push_back(&d1); v.push_back(&d2); v.push_back(&d2); v.push_back(&d1); w.push_back(d2); for_each(v.begin(), v.end(), mem_fn(& B::print)); cout<< endl << endl; for_each(w.begin(), w.end(), mem_fn(& B::print)); return 0; } same wrapper for both
STL Functor Concepts • Basic Functor Concepts • Generator • Unary Function • Binary Function • Adaptable Function Objects (turn functions into function objects) • Adaptable Generator • Adaptable Unary Function • Adaptable Binary Function • Predicates (return a boolean result) • Predicate • Binary Predicate • Adaptable Predicate • Adaptable Binary Predicate • Strict Weak Ordering • Specialized Concepts • Random Number Generator • Hash Function
STL Functor Concept Hierarchy Assignable • is-refined-by Generator Binary Function Unary Function Adaptable Generator Binary Predicate Adaptable Binary Function Random Number Generator Predicate Adaptable Unary Function Adaptable Predicate Hash Function Adaptable Binary Predicate Strict Weak Ordering • Basic Function Object • Predicate • Specialized • Adaptable Function Object
Assignable Concept • Does not refine any other STL concept • Valid Expressions • Copy Constructor X(x); X x(y); X x = y; • Assignment x = y; • Models of Assignable • Almost every non-const C++ built-in type … • … and function pointers … • … but not functions (cannot construct or assign them) • Here, all Basic Function Object concepts • Generator, Unary Function, Binary Function • And the concepts that specialize them
Generator Concept • Refines Assignable • Abstracts pointers to 0-ary functions (no arguments) • Valid Expressions • Function call signature with no arguments f() • Semantics • Returns some value of type Result • Different invocations may return different values • Or, can represent a constant as a 0-ary functor • Invocation may change the function object’s internal state • So, operator() need not be a const member function
Generator Example • Goal: fill a vector with random numbers • Generic generate algorithm • Fills in a range given in its 1st and 2nd arguments • applies Generator Concept to its 3rd argument • Here, the functor is simply a function pointer • To the <cmath> (0-ary) rand() function vector<int> v(100); generate(v.begin(), v.end(), rand);
Unary Function Concept • Also a refinement of Assignable • Valid Expression • Function call signature with one argument f(x) • May ignore or use single argument • Similar return, const semantics to generator • Pre-C++11 Example using unary_function struct sine : public unary_function<double,double> { double operator()(double x) const { return sin(x); } }; sine func; • C++11 Example using function function<double (double)> func(sin);
Binary Function Concept • Also a refinement of Assignable • Valid Expression • Function call signature with two arguments f(x,y) • May use or ignore either or both of its arguments • Similar const and return semantics to Unary Function • Pre-C++11 Example using binary_function struct exponentiate : public binary_function<double,double,double> { double operator()(double x, double y) const { return pow(x,y); } }; • C++11 Example using function function<double (double,double)> func(pow);
Adaptable Function Objects • Allow functors to be used with Function Object Adaptors • Associated types – of argument(s), and especially return value • How to access these associated types ? • Define Adaptable Function Object Concepts • Adaptable Generator F1::result_type • Adaptable Unary Function F2::argument_typeF2::result_type • Adaptable Binary Function F3::first_argument_typeF3::second_argument_typeF3::result_type • Models • Function pointers like Result(*f)(Arg) do not model these concepts • Helper adapters make Adaptable Function Objects from these functions • Pre-C++11 ptr_fun(f) or C++11 function<Result (Arg)>(f)
Adaptable Function Object Example • Each value printed out will be 3.0 larger than the corresponding element in v1 int main(int, char **) { constintvector_size = 10; vector<double> v1(vector_size); generate(v1.begin(), v1.end(), rand); // random values transform(v1.begin(), v1.end(), ostream_iterator<double>(cout, " "), bind1st(plus<double>(), 3.0)); transform(v1.begin(), v1.end(), ostream_iterator<double>(cout, " "), std::bind(plus<double>(), std::placeholders::_1, 3.0)); return 0; } Pre-C++11 function adapter Adaptable function objects C++11 function adapter
Predicate Concepts • Predicate • Refinement of Unary Function • Return type must be convertible to bool • Adaptable Predicate • Refinement of Predicate, Adaptable Unary Function • Adds typedefs for argument, return types • Binary Predicate • Refinement of Binary Function • Return type again must be convertible to bool • Adaptable Binary Predicate • Refinement of Binary Predicate, Adaptable Binary Function • Adds typedefs for the 2 arguments, return types • Strict Weak Ordering • Refinement of Binary Predicate (for comparison operations) • Similar semantics to operator< but with type constraints