For another example posts on functors (function objects) see here. A functor is an instance of a C++ class that has the operator()
defined. One big advantage of functors is that when you define the operator()
in C++ classes you not only get objects that can act like functions, but can also store state as well.
Non-stateful functor
Many of the example functors in C++ you see online are used as predicates, or comparison functions in STL algorithms. For example:
#include <iostream> #include <vector> #include <algorithm> class Foo { public: void operator () (int i) { std::cout << i << " "; } }; int main() { // Initialize example vector arrays and output int vals[] = { 1, 2, 2, 3, 2, 5, 77, 4, 16, 11 }; std::vector<int> v( vals, vals + sizeof( vals ) / sizeof( vals[ 0 ] ) ) ; for_each( v.begin(), v.end(), Foo() ); return 0; }
Giving the following output:
1 2 2 3 2 5 77 4 16 11
Stateful functor: collecting even numbers
Say in your application you had (say) a vector array of integers and you wished to find the indexes of all the even ones. This would be a perfect job for a functor and for_each
algorithm. The EvenNumberCollect
functor takes care to ensure that it’s instances are handled appropriately, hence the use of the copy constructor and assignment operator in addition to the default constructor:
#include <iostream> #include <vector> #include <algorithm> #include <iterator> #include <sstream> class EvenNumberCollect { public: std::vector<int> v; int index; // Default constructor EvenNumberCollect() : index( 0 ) {} // Copy constructor EvenNumberCollect( const EvenNumberCollect& e ) : index( e.index ) { v.clear(); v = e.v; } // Assignment operator EvenNumberCollect& EvenNumberCollect::operator =( const EvenNumberCollect &e ) { if ( &e != this ) { index = e.index; v.clear(); v = e.v; } return *this; } void Clear() { v.clear(); index = 0; } void operator() ( int i ) { if ( i % 2 == 0 ) { v.push_back( index ); } index++; } void PrintIndexes() { std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, "\t" ) ); std::cout << "\n"; } }; int main() { // Initialize example vector arrays and functors int vals1[] = { 1, 2, 2, 3, 2, 5, 77, 4, 16, 11 }; int vals2[] = { 5, 5, 3, 5, 5, 76, 5, 19 }; std::vector<int> v1( vals1, vals1 + sizeof( vals1 ) / sizeof( vals1[ 0 ] ) ) ; std::vector<int> v2( vals2, vals2 + sizeof( vals2 ) / sizeof( vals2[ 0 ] ) ) ; EvenNumberCollect evenNoColl1; EvenNumberCollect evenNoColl2; // Display the indexes of all even numbers evenNoColl1 = for_each( v1.begin(), v1.end(), evenNoColl1 ); evenNoColl1.PrintIndexes(); evenNoColl2 = evenNoColl1; evenNoColl2.PrintIndexes(); // Reset 1st functor and get all indexes of even numbers in second array evenNoColl1.Clear(); evenNoColl1 = for_each( v2.begin(), v2.end(), evenNoColl1 ); evenNoColl1.PrintIndexes(); return 0; }
Giving the following output showing the indexes of even numbers in the vector arrays:
1 2 4 7 8
1 2 4 7 8
5 8 9
Stateful functor: Appending strings from container items
This collects each ‘txt
‘ item in vector of ‘Token
‘ structs, appending them to the ‘txt
‘ member of the Appender
functor, which has the Word()
member function to return the complete concatenated string:
#include <iostream> #include <string> #include <vector> #include <algorithm> struct Token { std::string txt; Token( std::string s ) : txt( s ) {} }; class Append { private: std::string txt; public: Append() : txt("") {} // for_each calls operator() for each container element void operator() (const Token &x) { txt.append( x.txt + " " ); } std::string Word() const { return txt; } }; int main() { std::vector<Token> tokens; tokens.push_back( Token( "JJ" ) ); tokens.push_back( Token( "NN" ) ); tokens.push_back( Token( "NN" ) ); std::string txt = for_each( tokens.begin(), tokens.end(), Append() ).Word(); std::cout << txt << std::endl; return 0; }
Giving the following output:
JJ NN NN