Defining a Function Object
 


 
Ask the C++ Pro 10-Minute Solutions

Defining a Function Object
By Danny Kalev

Although pointers to functions are widely used for implementing function callbacks, C++ offers a significantly superior alternative to them, namely function objects. Function objects (also called "functors") are ordinary class objects that overload the () operator. Thus, syntactically, they behave like ordinary functions.

There are several advantages in using a function object instead of a pointer to function. First, they are more resilient to design changes because the object can be modified internally without changing its external interface. A function object can also have data members that store the result of a previous call. When using ordinary functions, you need to store the result of a previous call in a global or a local static variable. However, global and local static variables have some undesirable characteristics. Finally, compilers can inline a call made through a function object, thereby enhancing performance even further. In contrast, it is nearly impossible to inline a function call made through a pointer.

This solution will show how to define and use a function object that implements a negation operation. The first step consists of declaring an ordinary class and overloading the () operator:

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

The overloaded () might look a bit confusing because it has two pairs of parentheses. Remember that the first pair is always empty because it serves as the operator's name; the parameter list appears in the second pair of parentheses. 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 (it takes only a single operand), our overloaded () operator also takes a single parameter. The return type is identical to the parameter's type—int, in our example. The function body is trivial; it simply returns the negated argument.

Using the Function Object
We now define a function named Callback() to test our function object. Callback() takes two arguments: an int and a reference to Negate. Callback() treats neg, the function object, as if it were a function's name:

  #include <iostream>
  using std::cout;

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

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

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

In general, 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, we need a main() driver to pass arguments to Callback():

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

The program passes the integer 5 and a temporary Negate object to Callback(). As expected, the program displays -5.

Template Function Objects
Our example was confined to type int. However, one of the advantages of function objects is their generic nature. You can define the overloaded () operator as a member template so that it can work for any datatype: double, __int64 or char as follows:

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

  int main()
  {
   GenericNegate negate;
   cout<< negate(5.3333); // double
   cout<< negate(10000000000i64); // __int64
  }
Achieving this flexibility with ordinary callback functions is much more difficult.

Function Objects in the Standard Library
The C++ 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. 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
  } 
 

 
Other 10-Minute Solutions
 How to Change the Mouse Pointer without Flicker
 Setting Full Row Selection in ListView Control
 Automating Type Conversions with stringstream Objects
 Improving Memory Reallocation with Vectors
 How to Use <fstream> Classes for File I/O
 Casting About for Safe Typecasting
 Overloading Operator + the Right Way
 How to Create Persistent Objects
 Making Linked Lists More User-Friendly
 Preventing Glitches in Signal Processing
 Forcing Object Allocation on the Free-store
 Using String-Based Data Validation
 Implementing the 'Resource Acquisition Is Initialization' Idiom
 Simple Locks for Data Files
 Template Specializations
 Exception Handling
 Using Bit Fields in Data Optimization
 Using the Transform() Algorithm to Change a String's Case
 Use RTTI for Dynamic Type Identification
 Choosing the Right swap () Implementation
 Take Charge and Initialize Your Own Data
 Share Data Among Objects Using the Monostate Design Pattern
 String Manipulation Made Easy with std::string Algorithms
 Using typedef to Curb Miscreant Code
 Managing Objects' Construction Order
 Bitwise Operators: Combining Efficiency and Ease of Use
 Use Function Adapters to Extend Generic Algorithms' Usage
 Simplify Callback Dispatching with Enumerated Indexes
 Streamline Your Bulk I/O Operations with Stream Iterators
 Optimize Your Member Layout
 Preserve Code Safety with Conversion Operators
 Modify Your Base Class Interface in Derived Classes
 Tackle Common Programming Tasks Using the New <tuple> Library
 Use Local Classes for Proper Cleanup in Exception-enabled Apps
 Use multimap to Create Associative Containers with Duplicate Keys
 Enforcing Compile-time Constraints
 Facilitate Directory Operations with the <dirent.h> and <dir.h> Libraries
 Spruce Up Your Built-in Arrays
 Target 32- and 64-bit Platforms Together with a Few Simple Datatype Changes
 Restrict Object Allocation to Specific Memory Types
 Use the Pimpl Idiom to Reduce Compilation Time and Enhance Encapsulation
 Automate Resource Management with shared_ptr
 The Quick and Dirty Way to Add
 Pointing to Class Members
 Detecting Keystrokes While Your Application is Busy
 Linked Lists
 Programming the System Tray
 Create a "Universal" DLL
 Convert Path to Long Path Name
 Constructing an Object at a Pre-Determined Memory Position
 Declaring Classes and Member Functions in a Namespace
 Using the auto_ptr Class Template to Facilitate Dynamic Memory Management
 Using the random_shuffle() Algorithm to Randomize a Sequence of Elements
 Defining a Function Object
 Implementing the Singleton Design Pattern
 Declaring Function Pointers and Implementing Callbacks
 Overloading Operator << for a User-Defined Type
 Implementing a Stopwatch Class for Performance Measurements
 Creating and Accessing Environment Variables
 Executing an Object's Member Function in a Separate Thread
 Creating Heterogeneous Containers
 Overriding New and Delete
 Time and Date Manipulation
  Defining Functions with a Variable Argument List
 Optimize Abstract Operations with Function Templates




Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers