BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++ Tutorial - Functors(Function Objects) - 2020

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:




Functors (Function Objects)

Functors (Function Objects or Functionals) are simply put object + ().

In other words, a functor is any object that can be used with () in the manner of a function.

This includes normal functions, pointers to functions, and class objects for which the () operator (function call operator) is overloaded, i.e., classes for which the function operator()() is defined.

Sometimes we can use a function object when an ordinary function won't work. The STL often uses function objects and provides several function objects that are very helpful.

Function objects are another example of the power of generic programming and the concept of pure abstraction. We could say that anything that behaves like a function is a function. So, if we define an object that behaves as a function, it can be used as a function.

For example, we could define a struct names absValue that encapsulates the operation of converting a value of type float to its absolute valie:

#include <iostream>

struct absValue
{
	float operator()(float f) {
		return f > 0 ? f : -f;
	}
};

int main( ) 
{ 
	using namespace std;

	float f = -123.45;
	absValue aObj;
	float abs_f = aObj(f);
	cout << "f = " << f << " abs_f = " << abs_f << endl;
	return 0; 
}

Output is:

f = -123.45 abs_f = 123.45

As we see from the definition, even though aObj is an object and not a function, we were able to make a call on that object. The effect is to run the overloaded call operator defined by the object absValue. The operator takes a float value and returns its absolute value. Note that the function-call operator must be declared as a member function.

So, Objects of class types, like the absValue object, that define the call operator are referred to as function object.

Let's look at another example simulating a line. The class object is working as a functor taking x for a given y-intercept(b) and slope(a) giving us the corresponding y coordinate.

	y = ax + b

Here is the code:

#include <iostream>
using namespace std;

class Line {
	double a;	// slope
	double b;	// y-intercept

public:
	Line(double slope = 1, double yintercept = 1):
		a(slope),b(yintercept){} 
	double operator()(double x){
		return a*x + b;
	}
};

int main () {
	Line fa;			// y = 1*x + 1
	Line fb(5.0,10.0);		// y = 5*x + 10

	double y1 = fa(20.0);		// y1 = 20 + 1
	double y2 = fb(3.0);		// y2 = 5*3 + 10

	cout << "y1 = " << y1 << " y2 = " << y2 << endl;
	return 0;
}

Here y1 is calculated using the expression 1 * 20 + 1 and y2 is calculated using the expression 5 * 3 + 10. In the expression a *x + b, the values for b and a come from the constructor for the object, and the value of x comes from the argument to operator()().

Now that we have a little bit of taste for functor, let's step back and think. So, what's the behavior of a function? A functional behavior is something that we can call by using parentheses and passing arguments. For example:

	func(arg1, arg2);

If we want objects to behave this way, we have to make it possible to call them by using parentheses and passing arguments. It's not that difficult. All we have to do is to define operator () with the appropriate parameter types:

Class X {
public:
	// define "function call" operator
	return-value operator() (arguments) const;
	...
};

Then we can use object of this class to behave as a function that we can call:

X fn;
...
fn(arg1, arg2);	// call operator () for function object fn

This call is equivalent to:

fn.operator()(arg1,arg2);	// call operator () for function object fn

Here is a function object example.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Print {
public:
	void operator()(int elem) const {
		cout << elem << " ";
	}
};

int main () {
	vector<int> vect;
	for (int i=1; i<10; ++i) {
		vect.push_back(i);
	}

	Print print_it;
	for_each (vect.begin(), vect.end(), print_it);
	cout << endl;
	return 0;
}

The for_each function applied a specific function to each member of a range:

	for_each (vect.begin(), vect.end(), print_it);

In general, the 3rd argument could be a functor, not just a regular function. Actually, this raises a question. How do we declare the third argument? We can't declare it as a function pointer because a function pointer specifies the argument type. Because a container can contain just about any type, we don't know in advance what particular type should be used. The STL solves that problem by using template.

The class Print defines object for which we can call operator () with an int argument. The expression

print_it
in the statement
for_each (vect.begin(), vect.end(), print_it);
creates a temporary object of this class, which is passed to the for_each() algorithm as an argument. The for_each algorithm looks like this:
template<class Iterator, class Function>
Function for_each(Iterator first, Iterator last, Function f) {
	while (first != last) {
		f(*first);	
		++first;
	}
	return f;
}
for_each uses the temporary function f to call f(*first) for each element first. If the third parameter is an ordinary function, it simply calls it with *first as an argument. If the third parameter is a function object, it calls operator() for the function object f with *first as an argument. Thus, in this example for_each() calls:
print_it::operator()(*first)
The output is:
1 2 3 4 5 6 7 8 9

Here are some advantages of function object listed in "The C++ Standard Library" by Nicolai M. Josuttis.

  1. Function object are "smart functions."
    Objects that behave like pointers are smart pointers. This is similarly true for objects that behave like functions: They can be "smart functions" because they may have abilities beyond operator (). Function objects may have other member functions and attributes. This means that function objects have a state. ....
  2. Each function object has its own type.
    Ordinary functions have different types only when their signatures differ. However, function objects can have different types when their signatures are the same. In fact, each functional behavior defined by a function object has its own type. This is a significant improvement for generic programming using templates because you can pass functional behavior as a template parameter. ...
  3. Function objects are usually faster than ordinary functions.
    The concept of templates usually allows better optimization because more details are defined at compile time. Thus, passing function objects instead of ordinary functions often results in better performance.

Function object using nontype template parameter

A template nontype parameter is a constant value inside the template definition. We can use a nontype parameter when we need constant expressions. In the example, we need that to specify the size of an array. So, when arrayInit is called, the compiler figures out the value of the nontype parameter from the array argument.


Here is an example using integer as a non-type parameters. In the code below, we want to assign values for all the elements of a vector. So, we set the template parameter with an integer value as non-type, and as we traverse each element of the vector container, we set the value we want by calling setValue().

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

template <int val>
void setValue(int& elem)
{
	elem = val;
}

template <typename T>
class PrintElements
{
public:
	void operator()(T& elm) const {cout << elm << ' ';}
};

int main()
{
	int size = 5;
	vector<int> v(size);
        PrintElements<int> print_it;
	for_each(v.begin(), v.end(), print_it);
	for_each(v.begin(), v.end(), setValue<10>);
	for_each(v.begin(), v.end(), print_it);
	return 0;
}

Output:

0 0 0 0 0 10 10 10 10 10

Here is a similar example but this time it's adding some value to each element of the vector at runtime:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

template <typename T>
class Add
{
	T x;
public:
	Add(T xx):x(xx){}
	void operator()(T& e) const { e += x; }
};

template <typename T>
class PrintElements
{
public:
	void operator()(T& elm) const { cout << elm << ' ';}
};

int main()
{
	int size = 5;
	vector<int> v;
	for(int i = 0; i < size; i++) v.push_back(i);
	PrintElements<int> print_it;
	for_each(v.begin(), v.end(), print_it); cout << endl;
	for_each(v.begin(), v.end(), Add<int>(10));
	for_each(v.begin(), v.end(), print_it); cout << endl;
	for_each(v.begin(), v.end(), Add<int>(*v.begin()));
	for_each(v.begin(), v.end(), print_it);

	return 0;
}

Output:

0 1 2 3 4
10 11 12 13 14
20 21 22 23 24

The first call to Add(10) creates a Add type temporary object which is initialized with x = 10. Then, it adds its member value 10 to each of the element of the vector v while traversing. The second call to Add(*v.begin()) sets the member of the temporary object to v.begin() element, and then adds that value to each of the element of the vector.



Predicates

STL refines functor concepts as follows:

  1. A generator is a functor that can be called with no argument.
  2. A unary function is a functor that can be called with one argument.
  3. A binary function is a functor that can be called with two arguments.

The print_it functor for for_each() we used in the previous section is a unary function because it is applied to one container element at a time.

A special auxiliary function for algorithm is a predicate. Predicates are functions that return a Boolean value (or something that can be implicitly converted to bool). In other words, a predicate class is a functor class whose operator() function is a predicate, i.e., its operator() returns true or false.

Predicates are widely used in the STL. The comparison functions for the standard associative containers are predicates, and predicate functions are commonly passed as parameters to algorithms like find_if. Depending on their purpose, predicates are unary or binary.

  1. A unary function that returns a bool value is a predicate.
  2. A binary function that returns a bool value is a binary predicate.


Predefined Functions Objects

A typical example of predefined function object in STL is set. It sorts element in increasing order because by default it's using less(<) sorting criteria. So, when we do:

set<int> mySet;

internal code looks like this:

set<int, less<int> > mySet;

So, we want to store our elements in decreasing order in a set, we do:

set<int, greater<int> > mySet;

In the example below, the negate<int>() creates a function object from the predefined template class negate. It simply returns the negated int element.

The second transform() algorithm combines the elements from two vector containers by using multiplies<int>() operation. Then it writes the results into the 3rd vector container.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

template <typename T>
class PrintElements
{
public:
	void operator()(T& elm) const { cout << elm << ' ';}
};

int main()
{
	PrintElements<int> print_it;

	int size = 5;
	vector<int> v;
	for(int i = 0; i < size; i++) v.push_back(i);
	for_each(v.begin(), v.end(), print_it); cout << endl;

	transform(v.begin(), v.end(),   // source
		  v.begin(),            // destination
		  negate<int>());       // operation

	for_each(v.begin(), v.end(), print_it); cout << endl;

	transform(v.begin(), v.end(),   // source
		  v.begin(),            // second source
		  v.begin(),            // destination
		  multiplies<int>());   // operation

	for_each(v.begin(), v.end(), print_it); cout << endl;

	return 0;
}

Output:

0 1 2 3 4
0 -1 -2 -3 -4
0 1 4 9 16

Here are the two types of transform() algorithms:

  1. Transforming elements
      OutputIterator
      transform ( InputIterator source.begin(), InputIterator source.end(),
                  OutputIterator destination.begin(),
                  UnaryFunc op )
      
  2. Combining elements of two sequences
      OutputIterator
      transform ( InputIterator1 source1.begin(), InputIterator1 source1.end(),
                  InputIterator2 source2.begin()
                  OutputIterator destination.begin(),
                  BinaryFunc op )
      


Function Adapter

Function adapter converts some other interface to an interface used by STL.


bind2nd()

Here is the declaration of bind2nd():

template <class Operation, class T>
  binder2nd<Operation> bind2nd (const Operation& op, const T& x);

It returns function object with second parameter bound. This function constructs an unary function object from the binary function object op by binding its second parameter to the fixed value x.

The function object returned by bind2nd() has its operator() defined such that it takes only one argument. This argument is used to call binary function object op with x as the fixed value for the second argument.

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

struct PrintElm
{
	void operator()(int & elm) const { cout << elm << ' ';}
};

int main()
{
	int size = 10;
	vector<int> v;
	for(int i = 0; i < size; i++) v.push_back(i);
	for_each(v.begin(), v.end(), PrintElm()); cout << endl;
	replace_if(v.begin(), v.end(), 
		   bind2nd(equal_to<int>(),0), 
	           101);
	for_each(v.begin(), v.end(), PrintElm()); cout << endl;
	v.erase(
		remove_if(v.begin(), v.end(), 
		           bind2nd(less<int>(), 3)), 
		v.end()
		);
	for_each(v.begin(), v.end(), PrintElm()); cout << endl;
	transform(v.begin(), v.end(),
	          ostream_iterator<int>(cout, " "),           
		  negate<int>()); 
	return 0;
}

Output:

0 1 2 3 4 5 6 7 8 9
101 1 2 3 4 5 6 7 8 9
101 3 4 5 6 7 8 9
-101 -3 -4 -5 -6 -7 -8 -9

Here are additional examples of bind2nd():

  1. To count all the elements within a vector that are less than or equal to 100, we can use count_if():
    	count_if(vec.begin(), vec.end(),
    			bind2nd(less_equal<int>(), 100));
    
    The 3rd argument uses bind2nd() function adaptor. This adaptor returns a function object that applies the <= operator using 100 as the right hand operand. So, the call to count_if() counts the number of elements in the input range (from vec.begin() to vec.end()) that are less than or equal to 100.

  2. We can negate the binding of less_equal:
    	count_if(vec.begin(), vec.end(),
    			not1(bind2nd(less_equal<int>(), 100)));
    
    As in the previous sample, we first bind the second operand of the less_equal object to 100, transforming that binary operation into a unary operation. Then, we negate the return from the operation using not1. So, what it does is that each element will be tested to see if it is <= 100. Then, the truth value of the result will be negated. Actually, the call counts those elements that are not <= 100.


Here is another sample of using bind2nd() with transform(). The code multiplies 10 to each element of an array and outputs them in Print() function:

#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename ForwardIter>
void Print(ForwardIter first, ForwardIter last, const char* status)
{
  cout << status << endl;
  while(first != last)
    cout << *first++ << " ";
  cout << endl;

}

int main()
{
  int arr[] = {1, 2, 3, 4, 5};

  Print(arr, arr+5, "Initial values");

  transform(arr, arr+5, arr, bind2nd(multiplies<int>(), 10));

  Print(arr, arr+5, "New values:");

  return 0;
}

Output:

$ g++ -o bind bind.cpp
$ ./bind
Initial values
1 2 3 4 5 
New values:
10 20 30 40 50 





Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master