Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

Adaptable function objects

Standard function adaptors such as bind1st( ) and bind2nd( ) make some assumptions about the function objects they process. Consider the following expression from the last line of the earlier CountNotEqual.cpp program:

not1(bind1st(equal_to<int>(), 20))
 

The bind1st( ) adaptor creates a unary function object of type binder1st, which simply stores an instance of equal_to<int> and the value 20. The binder1st::operator( ) function needs to know its argument type and its return type; otherwise, it will not have a valid declaration. The convention to solve this problem is to expect all function objects to provide nested type definitions for these types. For unary functions, the type names are argument_type and result_type; for binary function objects they are first_argument_type, second_argument_type, and result_type. Looking at the implementation of bind1st( ) and binder1st in the <functional> header reveals these expectations. First inspect bind1st( ), as it might appear in a typical library implementation:

template<class Op, class T>
binder1st<Op> bind1st(const Op& f, const T& val) {
typedef typename Op::first_argument_type Arg1_t;
return binder1st<Op>(f, Arg1_t(val));
}
 

Note that the template parameter, Op, which represents the type of the binary function being adapted by bind1st( ), must have a nested type named first_argument_type. (Note also the use of typename to inform the compiler that it is a member type name, as explained in Chapter 5.) Now see how binder1st uses the type names in Op in its declaration of its function call operator:

// Inside the implementation for binder1st<Op>
typename Op::result_type
operator()(const typename Op::second_argument_type& x)
const;
 

Function objects whose classes provide these type names are called adaptable function objects.

Since these names are expected of all standard function objects as well as of any function objects you create to use with function object adaptors, the <functional> header provides two templates that define these types for you: unary_function and binary_function. You simply derive from these classes while filling in the argument types as template parameters. Suppose, for example, that we want to make the function object gt_n, defined earlier in this chapter, adaptable. All we need to do is the following:

class gt_n : public unary_function<int, bool> {
int value;
public:
gt_n(int val) : value(val) {}
bool operator()(int n) {
return n > value;
}
};
 

All unary_function does is to provide the appropriate type definitions, which it infers from its template parameters as you can see in its definition:

template<class Arg, class Result> struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
 

These types become accessible through gt_n because it derives publicly from unary_function. The binary_function template behaves in a similar manner.

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire