Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Function Objects

Last updated Jan 1, 2003.

Ordinary pointers to functions are widely used for implementing callbacks of C functions and static member functions (for further information on that subject, see the Pointers to Functions section). Yet, C++ offers a significantly superior alternative, namely function objects. In essence, function objects (also called functors) are ordinary class objects that overload the () operator. Thus, while syntactically they behave like ordinary functions, under the hood they are real objects.

There are several advantages in using a function object instead of pointers to functions or pointers to members. First, function objects are more resilient to design changes, because the object can be modified internally without changing its external interface. While the same can be said about ordinary functions whose prototypes remain intact when their implementation changes, a function object may contain data members that store its state, thereby eliminating the need of external variables and complex argument lists.

Function objects offer another benefit in regard to performance. Compilers are usually able to inline a call made through a function object, thereby optimizing your code. By contrast, it is harder to inline a function call made through a pointer.

Implementing a Function Object

Suppose you want to define a function object that implements a negation operation. The first step consists of declaring an ordinary class and overloading the () operator, as follows:

class Negate 
{
public: 
 int operator() (int n);
};

The overloaded () might be a bit confusing because it has two pairs of parentheses. However, remember that the first pair is always empty, because it serves as the operator's name; the second pair of parentheses contains the parameter list (unlike other overloaded operators, whose number of parameters is fixed, the overloaded () operator may take any number of parameters).

Because the built-in negation operator is unary (that is, it takes a single operand), your overloaded () operator also takes a single parameter in this case. The return type is identical to the parameter's type -- int, in this example. The function body is trivial; it simply returns the argument's negative value.

int Negate::operator()(int n)
{ 
 return -n;
} 

Using the Function Object

To test this function object, let's define a test function called test() which takes two arguments: an int and a reference to Negate. test() treats the function object as if it were a function's name:

#include <iostream>
using std::cout;

void test(int n, Negate & neg) 
{
 int val = neg(n); //1 invoke overloaded () 
 cout << val <<std::endl;
}

Don't let the syntax mislead you: neg is an object, not a function. The compiler silently transforms the line numbered 1 into the following member function call:

int val = neg.operator()(n);

In most cases, function objects do not define constructors and destructors. Therefore, they do not incur any overhead during their creation and destruction. As previously noted, the compiler can inline the overloaded operator's code, thereby avoiding the runtime overhead associated with a full-blown function call.

To complete the example, use the following main() driver to pass arguments to test():

int main() 
{
 test(5, Negate() ); //output -5
}

The program passes to test() the integer 5 and a reference to a temporary Negate object. As expected, the output is -5.

Template Function Objects

The previous example was confined to type int. However, one advantage of function objects is their generic nature. You can define the overloaded () operator as a member template (for further information, read the Templates section) so that it will apply to all numeric datatype: long, double, std::complex and so on. For example:

class GenericNegate
{
public: 
 template <class T> T operator() (T t) const {return -t;}
};

int main()
{
 GenericNegate negate;
 __int64 val = 10000000000i64;
 cout<< negate(5.3333); // double
 cout<< negate(val); // __int64
}

Now try to imagine how difficult and dangerous it would be to achieve the same functionality by using ordinary pointers to functions.

Function Objects in the Standard Library

The Standard Library defines several useful function objects that can be plugged into STL algorithms. For example, the sort() algorithm takes a predicate object as its third argument. (A predicate object is a templatized function object that returns a Boolean result; learn more about them in the The Standard Template Library (STL) and Generic Programming section.) You can pass the predicates greater<> or less<> to sort() to force a descending or ascending sorting order, respectively:

#include <functional> // for greater<> and less<>
#include <algorithm> //for sort() 
#include <vector>
using namespace std;
 
int main()
{ 
 vector <int> vi;
 //..fill vector
 sort(vi.begin(), vi.end(), greater<int>() );//descending 
 sort(vi.begin(), vi.end(), less<int>() ); //ascending
} 

Summary

Function objects provide the functionality of pointers to functions and pointers to member functions while offering additional benefits such as a generic interface, tighter encapsulation (by means of having internal data members that store the object's state) and enhanced performance. Predicates are a special kind of function objects that return a Boolean result. They are used in the Standard Library and elsewhere to manipulate the behavior of certain algorithms.