Chapter 9: More Operator Overloading

We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.

Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.

All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.

Having covered the overloaded assignment operator in chapter 7, and having shown several examples of other overloaded operators as well (i.e., the insertion and extraction operators in chapters 3 and 5), we will now take a look at several other interesting examples of operator overloading.

9.1: Overloading `operator[]()'

As our next example of operator overloading, we present a class which is meant to operate on an array of ints. Indexing the array elements occurs with the standard array operator [], but additionally the class checks for boundary overflow. Furthermore, the index operator ( operator[]()) is interesting in that it both produces a value and accepts a value, when used, respectively, as a right-hand value ( rvalue) and a left-hand value ( lvalue) in expressions. An example showing the use of the class is:
    int main()
    {
        IntArray
            x(20);                      // 20 ints

        for (int i = 0; i < 20; i++)
            x[i] = i * 2;               // assign the elements
                                
        for (int i = 0; i <= 20; i++)   // produeces boundary overlflow
            cout << "At index " << i << ": value is " << x[i] << endl;
    }
First, the constructor is used to create an object containing 20 ints. The elements stored in the object can be assigned or retrieved: the first for-loop assigns values to the elements using the index operator, the second for-loop retrieves the values, but will also produce a run-time error as the non-existing value x[20] is addressed. The IntArray class interface is:
    class IntArray
    {
        int 
            *d_data;
        unsigned
            d_size;

         public:
            IntArray(unsigned size = 1);
            IntArray(IntArray const &other);
            ~IntArray();
            IntArray const &operator=(IntArray const &other);

                                                // overloaded index operators:
            int &operator[](unsigned index);        // first
            int operator[](unsigned index) const;   // second
        private:
            void boundary(unsigned index) const;
            void copy(IntArray const &other);
    };
This class has the following characteristics: Now, the implementation of the members are:
    #include "intarray.ih"

    IntArray::IntArray(unsigned size)
    :
        d_size(size)
    {
        if (d_size < 1)
        {
            cerr << "IntArray: size of array must be >= 1\n";
            exit(1);
        }
        d_data = new int [d_size];
    }

    IntArray::IntArray(IntArray const &other)
    {    
        copy(other);
    }
    
    IntArray::~IntArray()
    {    
        delete d_data;
    }
    
    IntArray const &IntArray::operator=(IntArray const &other)
    {
        if (this != &other)
        {
            delete d_data;
            copy(other);
        }
        return *this;
    }

    void IntArray::copy(IntArray const &other)
    {
        d_size = other.d_size;
        d_data = new int [d_size];
        memcpy(d_data, other.d_data, d_size * sizeof(int));
    }

    int &IntArray::operator[](unsigned index)
    {
        boundary(index);
        return d_data[index];   
    }

    int IntArray::operator[](unsigned index) const
    {
        boundary(index);
        return d_data[index];
    }

    void IntArray::boundary(unsigned index) const
    {
        if (index >= d_size)
        {
            cerr << "IntArray: boundary overflow, index = " <<
                    index << ", should range from 0 to " << d_size - 1 << endl;
            exit(1);
        }
    }

9.2: overloading the insertion and extraction operators

This section describes how a class can be adapted in such a way that it can be used with the C++ streams cout and cerr and the insertion operator operator<<(). Adapting a class in such a way that the istream's extraction operator ( operator>>()) can be used, occurs in a similar way and is simply shown in an example.

The implementation of an overloaded operator<<() in the context of cout or cerr involves their class, which is ostream. This class is declared in the header file iostream and defines only overloaded operator functions for `basic' types, such as, int, char *, etc.. The purpose of this section is to show how an insertion operator can be overloaded in such a way that an object of any class, say Person (see chapter 7), can be inserted into an ostream. After making available such an overloaded operator, the following will be possible:

    Person
        kr("Kernighan and Ritchie", "unknown", "unknown");

    cout << "Name, address and phone number of Person kr:\n" << kr << endl;
The statement cout << kr involves operator<<(). This member function has two operands: an ostream & and a Person &. The proposed action is defined in an overloaded global operator operator<<() expecting two arguments:
                                // assume declared in `person.h'
    ostream &operator<<(ostream &, Person const &);

                                // define in some source file
    ostream &operator<<(ostream &stream, Person const &pers)
    {
        return 
            stream << 
                "Name:    " << pers.getName() <<
                "Address: " << pers.getAddress() <<
                "Phone:   " << pers.getPhone();
    }
Note the following characteristics of operator<<():

In order to overload the extraction operator for, e.g., the Person class, members are needed to modify the private data members. Such modifiers are normally included in the class interface. For the Person class, the following members should be added to the class interface:

    void setName(char const *name);
    void setAddress(char const *address);
    void setPhone(char const *phone);
The implementation of these members could be straightforward: the memory pointed to by the corresponding data member must be deleted, and the data member should point to a copy of the text pointed to by the parameter. E.g.,
    void Person::setAddress(char const *address)
    {
        delete d_address;
        d_address = strdupnew(address);
    }
A more elaborate function could also check the reasonableness of the new address. This elaboration, however, is not further pursued here. Instead, let's have a look at the final overloaded extraction operator ( operator>>()). A simple implementation is:
    istream &operator>>(istream &str, Person &p)
    {
        string
            name, 
            address,
            phone;

        if (str >> name >> address >> phone)    // extract three strings
        {
            p.setName(name.c_str());
            p.setAddress(address.c_str());
            p.setPhon(phone.c_str());
        }
        return str;                       
    }
Note the stepwise approach that is followed with the extraction operator: first the required information is extracted, using available extraction operators (like a string-extraction), then, if that succeeds, modifier members are used to modify the data members of the object to be extracted. Finally, the stream object itself is returned as a reference.

9.3: Conversion operators

A class may be constructed around a basic type. E.g., the class string was constructed around the char * type. Such a class may define all kinds of operations, like assignments. Take a look at the following class interface, designed after the string class:
    class String 
    {
        char
            *d_string;
        public:
            String();
            String(char const *arg);
            ~String();            
            String(String const &other);
            String const &operator=(String const &rvalue);
            String const &operator=(char const *rvalue);
    };
Objects from this class can be initialized from a char const *, and also from a String itself. There is an overloaded assignment operator, allowing the assignment from a String object and from a char const * (Note that the assingment from a char const * also includes the null-pointer. An assignment like stringObject = 0 is perfectly in order.).

Usually, in classes that are less directly linked to their data than this String class, there will be an accessor member function, like char const *String::c_str() const. However, the need to use this latter member doesn't appeal to our intuition when an array of String objects is defined by, e.g., a class StringArray. If this latter class provides the operator[] to access individual String members, we would have the following interface for StringArray:

    class StringArray 
    {
        String
            *d_store;
        unsigned
            d_n;
        public:
            StringArray(unsigned size);
            StringArray(StringArray const &other);
            StringArray const &operator=(StringArray const &rvalue);
            ~StringArray();            
    
            String &operator[](unsigned index);
    };
Using the StringArray::operator [], assignments between the String elements can simply be realized:
    StringArray
        sa(10);             
    
    sa[4] = sa[3];  // String to String assignment
It is also possible to assign a char const * to an element of sa:
sa[3] = "hello world";

Here, the following steps are taken:

Now we try to do it the other way around: how to access the char const * that's stored in sa[3]? We try the following code:
    char const
        *cp = sa[3];
This, however, won't work: we would need an overloaded assignment operator for the 'class char const *'. Unfortunately, there isn't such a class, and therefore we can't build that overloaded assignment operator (see also section 9.10). Furthermore, casting won't work: the compiler doesn't know how to cast a String to a char const *. How to proceed from here?

The naive solution is to resort to the accessor member function c_str():

cp = sa[3].c_str()

That solution would work, but it looks so clumsy.... A far better approach would be to use a conversion operator.

A conversion operator is a kind of overloaded operator, but this time the overloading is used to cast the object to another type. Using a conversion operator a String object may be interpreted as a char const *, which can then be assigned to another char const *. Conversion operators can be implemented for all types for which a conversion is needed.

In the current example, the class String would need a conversion operator for a char const *. In class interfaces, the general form of a conversion operator is:

operator <type>();

In our String class, this would become:

operator char const *();

The implementation of the conversion operator is straightforward:

    String::operator char const *()
    {
        return d_string;
    }
Notes:

One might wonder what will happen if an object for which, e.g., a string conversion operator is defined is inserted into, e.g., an ostream object, into which string objects can be inserted. In this case, the compiler will not look for appropriate conversion operators (like operator string()), but will report an error. For example, the following example procedure a compilation error:

    #include <iostream>
    #include <string>
    using namespace std;

    class NoInsertion
    {
        public:
            operator string() const;
    };

    int main()
    {
        NoInsertion
            object;

        cout << object << endl;
    }
            
The problem is caused by the fact that the compiler notices an insertion, applied to an object. It will now look for an appropriate overloaded version of the insertion operator. As it can't find one, it reports a compilation error, instead of performing a two-stage insertion: first using the operator string() insertion, followed by the insertion of that string into the ostream object. Conversion operators are used when the compiler is given no choice: an assignment of a NoInsertion object to a string object is such a situation. The problem of how to insert an object into, e.g., an ostream is simply solved: by defining an appropriate overloaded insertion operator, rather than by resorting to a conversion operator.

9.4: The `explicit' keyword

Conversions are not only performed by conversion operators, but also by constructors having one parameter (or multiple parameters, having default parameter values beyond the first parameter).

Consider the class Person introduced in chapter 7. This class has a constructor

Person(char const *name, char const *address, char const *phone)

This constructor could be given default parameter values:

    Person(char const *name, char const *address = "<unknwon>",
                             char const *phone = "<unknown>");
In several situations this constructor might be used intentionally, possibly providing the default <unknown> texts for the address and phone numbers. For example:
    Person
        frank("Frank", "Room 352", "050 363 3688");
Also, functions might use Person objects as parameters, e.g., the following member in a fictitious class PersonData could be available:
    PersonData &PersonData::operator+=(Person const &person);
Now, combining the above two pieces of code, we might, do something like
    PersonData
        dbase;

    dbase += frank;     // add frank to the database
So far, so good. However, since the Person constructor can also be used as a conversion operator, it is also possible to do:
    dbase += "karel";
Here, the char const * text `karel' is converted to an (anonymous) Person object using the former Person constructor: the second and third parameters use their default values. Here, and implicit conversion is performed from a char const * to a Person object, which might not be what the programmer had in mind when the class Person was constructed.

As another example, consider the situation where a class representing a container is constructed. Let's assume that the initial construction of objects of this class is rather complex and time-consuming, but expanding an object so that it can accomodate more elements is even more time-consuming. Such a situation might arise when a hash-table is initially constructed to contain n elements: that's ok as long as the table is not full, but when the table must be expanded, all its elements normally must be rehashed according to the new table size.

Such a class could (partially) be defined as follows:

    class HashTable
    {
        public:
            HashTable(unsigned n);  // n: initial table size
            unsigned size();        // returns current # of elements

                                    // add new key and value
            void add(string const &key, string const &value);
    };
Now consider the following implementation of add():
    void HashTable::add(string const &key, string const &value)
    {
        if (size() > d_n * 0.75)    // table gets rather full
            d_ht = size() * 2;      // Oops: not what we want!

        // etc.
    }
In the first line of the body of add() the programmer first determines how full the hashtable currently is: if it's more than three quarter full, then the intention is to double the size of the hashtable. This eventually succeeds, but the cost is way too high: accidentally the programmer assigns an unsigned value, intending to tell the hashtable what its new size should be. What happens next comes as a bit of a surprise: If an implicit use of a constructor is not appropriate (or dangerous), it can be prevented by using the explicit modifier with the constructor. Constructors using the explicit modifier can only be used for the explicit construction of objects, and cannot be used as implicit type convertors anymore. For example, to prevent the implicit conversion from char const * to Person the class interface of the class Person must contain the constructor
    explicit Person(char const *name, char const *address = "<unknwon>",
                                      char const *phone = "<unknown>");
Note, that it is still possible to add `karel' to the dbase object, but it can't be realized through an implicit conversion anymore. To add `karel' to the database, the Person constructor must be explicitly called, creating an anonymous object:
    dbase += Person("karel");
Similarly, the assignment ht = size() * 2 is now caught by the compiler, complaining with a message like no match for `HashTable& = unsigned int' operator.

9.5: Overloading increment and decrement

Overloading the increment operator ( operator++()) and decrement operator ( operator--()) creates a little problem: there are two version of each operator, as they may be used as postfix operator (e.g., x++) or as prefix operator (e.g., ++x).

Used as postfix operator, the value's object is returned as rvalue, so we have an expression that has a fixed value: the post-incremented variable itself disappears from view. Used as prefix operator, the variable is incremented, and its value is returned as lvalue, so it can be altered immediately again. Whereas these characteristics are not required when the operator is overloaded, it is strongly advised to implement these characteristics in any overloaded increment or decrement operator.

Suppose we define a wrapper class around the unsigned value type. The class could have the following (partially shown) interface:

    class Unsigned
    {
        unsigned
            d_value;
        public:
            Unsigned();
            Unsigned(unsigned init);
            Unsigned &operator++();
    }
This defines the prefix overloaded increment operator. An lvalue is returned, as we can deduce from the return type, which is Unsigned &.

The implementation of the above function could be:

    Unsigned &Unsigned::operator++()
    {
        ++d_value;
        return *this;
    }
In order to define the postfix operator, an overloaded version of the operator is defined, expecting an int argument. This might be considered a kludge, or a consequent application of the notion of overloaded functions. Whatever your opinion in this matter, we can conclude the following: To add the postfix increment operator to the Unsigned wrapper class, add the following line to the class interface:
Unsigned &operator++(int);

The implementation of the postfix increment operator should be like this:

    Unsigned Unsigned::operator++(int)
    {
        return d_value++;
    }
The simplicity of this implementation is deceiving, however. Note that: When the object has a more complex data organization, the use of a copy constructor might be indicated. For instance, assume we want to implement the postfix increment operator in the class PersonData, mentioned in section 9.4. Presumably, the PersonData class contains a complex inner data organization. If the PersonData class would maintain a pointer Person *current to the Person object that is currently selected, then the postfix increment operator for the class PersonData could be implemented as follows:
    PersonData PersonData::operator++(int)
    {
        PersonData
            tmp(*this);

        incrementCurrent();     // increment `current', somehow.
        return tmp;
    }
A matter of concern here could be that this operation actually requires two calls to the copy constructor: first to keep the current state, then to copy the tmp object to the (anonymous) return value. In some cases this double call of the copy constructor might be avoidable, by defining a specialized constructor. E.g.,
    PersonData PersonData::operator++(int)
    {
        return PersonData(*this, incrementCurrent());
    }
Here, incrementCurrent() is supposed to return the information which allows the constructor to set its current data member to the pre-increment value, while at the same time incrementing current of the actual PersonData object. The above constructor would have to: At the same time, incrementCurrent() would have incremented current of the actual PersonData object.

The general rule is that double calls of the copy constructor can be avoided if a specialized constructor can be defined which initializes an object to the pre-increment state of the current object. The current object itself has its necessary data members incremented by a function, whose return value is passed as argument to the constructor, thereby informing the constructor of the pre-incremented state of the involved data members. The postfix increment operator will then return the thus constructed (anonymous) object, and no copy constructor is ever called.

Finally it is noted that the call of the increment or decrement operator using its overloaded function name might require us to provide a (any) int argument to inform the compiler that we want the postfix increment function. E.g.,

    PersonData
        p;

    p = other.operator++();     // incrementing `other', then assigning `p'
    p = other.operator++(0);    // assigning `p', then incrementing `other'

9.6: Overloading `operator new(size_t)'

If the operator new is overloaded, it must have a void * return type, and at least an argument of type size_t. The size_t type is defined in the header file cstddef, which must therefore be included when the operator new is overloaded.

It is also possible to define multiple versions of the operator new, as long as each version has its own unique set of arguments. The global new operator can still be used, through the ::-operator. If a class X overloads the operator new, then the system-provided operator new is activated by

X *x = ::new X();

Overloading new[] is discussed in section 9.8. The following example shows an overloaded version of operator new:

    #include <cstddef>

    void *X::operator new(size_t sizeofX)
    {
        void
            *p = new char[sizeofX];
            
        return memset(p, 0, sizeof(X));
    } 
Now, let's see what happens when operator new is overloaded for the class X. Assume that class is defined as follows (For the sake of simplicity we have violated the principle of encapsulation here. The principle of encapsulation, however, is immaterial to the discussion of the workings of the operator new.):
    class X
    {
        public:
            void *operator new(size_t sizeofX);

            int d_x
            int d_y;
    };
Now, consider the following program fragment:
    #include "x.h"  // class X interface 
    #include <iostream>    
    using namespace std;

    int main()
    {
        X
            *x = new X();
            
        cout << x->d_x << ", " << x->d_y << endl;
    }
This small program produces the following output:
0, 0

At the call of new X(), our little program performed the following actions:

Due to the initialization of the block of memory by operatornew the allocated X object was already initialized to zeros when the constructor was called.

Non-static member functions are passed a (hidden) pointer to the object on which they should operate. This hidden pointer becomes the this pointer in non-static member functions. This procedure is also followed by the constructor. In the next pieces of pseudo C++ code, the pointer is made visible. In the first part an X object x is defined directly, in the second part of the example the (overloaded) operator new is used:

    X::X(&x);                   // x's address is passed to the constructor

    void                        // new allocates the memory for an X object
        *ptr = X::operator new();

    X::X(ptr);                  // now let the constructor operate on the
                                // memory returned by 'operator new'
Notice that in the pseudo C++ fragment the member functions were treated as static member function of the class X. Actually, operator new is a static member function of its class: it cannot reach data members of its object, since it's normally the task of the operator new first to create room for that object. It can do that by allocating enough memory, and by initializing the area as required. Next, the memory is passed to the constructor (as the this pointer) for further processing. The fact that an overloaded operator new is actually a static function, not requiring an object of its class, can be illustrated in the following (frowned upon in normal situations!) program fragment, which can be compiled without problems (assume class X has been defined and is available as before):
    int main()
    {
        X
            x;

        X::operator new(sizeof x);
    }
The call to X::operator new() returns a void * to an initialized block of memory, the size of an X object.

The operator new can have multiple parameters. The first parameter is initialized by an implicit argument and is always the size_t parameter, other parameters are initialized by explicit arguments that are specified when operator new is used. For example:

    class X
    {
        public:
            void *operator new(size_t p1, unsigned p2);
            void *operator new(size_t p1, char const *fmt, ...);
    };
    
    int main()
    {
        X
            *p1 = new(12) X(),
            *p2 = new("%d %d", 12, 13) X(),
            *p3 = new("%d", 12) X();
    }
The pointer p1 is a pointer to an X object for which the memory has been allocated by the call to the first overloaded operator new, followed by the call of the constructor X() for that block of memory. The pointer p2 is a pointer to an X object for which the memory has been allocated by the call to the second overloaded operator new, followed again by a call of the constructor X() for its block of memory. Notice that pointer p3 also uses the second overloaded operator new(), as that overloaded operator accepts a variable number of arguments, the first of which is a char const *.

9.7: Overloading `operator delete(void *)'

The delete operator may be overloaded too. The operator delete must have a void * argument, and an optional second argument of type size_t, which is the size in bytes of objects of the class for which the operator delete is overloaded. The returntype of the overloaded operator delete is void.

Therefore, in a class the operator delete may be overloaded using the following prototype:

void operator delete(void *);

or

void operator delete(void *, size_t);

Overloading delete[] is discussed in section 9.8.

The `home-made' operator delete is called after executing the destructor of the associated class. So, the statement

delete ptr;

with ptr being a pointer to an object of the class X for which the operator delete was overloaded, boils down to the following statements:

    X::~X(ptr);     // call the destructor function itself

                    // and do things with the memory pointed to by ptr
    X::operator delete(ptr, sizeof(*ptr));
The overloaded operator delete may do whatever it wants to do with the memory pointed to by ptr. It could, e.g., simply delete it. If that would be the preferred thing to do, then the default delete operator can be activated using the :: scope resolution operator. For example:
    void X::operator delete(void *ptr)
    {
        // any operation considered necessary, then:
        ::delete ptr; 
    }

9.8: Operators `new[]' and `delete[]'

In sections 7.1.1, 7.1.2 and 7.2.1 operator new[] and operator delete[] were introduced. Like operator new and operator delete the operators new[] and delete[] may be overloaded. Because it is possible to overload new[] and delete[] as well as operator new and operator delete, one should be careful in selecting the appropriate set of operators. The following rule of thumb should be followed:
If new is used to allocate memory, delete should be used to deallocate memory. If new[] is used to allocate memory, delete[] should be used to deallocate memory.

The default way these operators act is as follows:

Operators new[] and delete[] may only be overloaded in classes. Consequently, when allocating primitive types or pointers to objects only the default line of action is followed: when arrays of pointers to objects are deleted, a memory leak occurs unless the objects to which the pointers point were deleted earlier.

In this section the mere syntax for overloading operators new[] and delete[] is presented. It is left as an exercise to the reader to make good use of these overloaded operators.

To overload operator new[] in a class Object the interface should contain the following lines, showing multiple forms of overloaded forms of operator new[]:

    class Object
    {
        public:
            void *operator new[](size_t size);                  
            void *operator new[](size_t index, unsigned extra); 
    };
The first form shows the basic form of operator new[]. It should return a void *, and defines at least a size_t parameter. When operator new[] is called, size contains the number of bytes that must be allocated for the required number of objects. These objects can be initialized by the global operator new[] using the form
::new Object [size / sizeof(Object)]

Alternatively, using

::new char [size]

the required (uninitialized) amount of memory can be allocated too. An example of an overloaded operator new[] member function, returning an array of Object objects all filled with 0-bytes, is:

    void *Object::operator new[](size_t size)
    {
        return memset(new char[size], 0, size);
    }
Having constructed the overloaded operator new[], it will be used automatically in statements like:
    Object
        *op = new Object[12];
Operator new[] may be overloaded using extra parameters. The second form of the overloaded operator new[] shows such an extra unsigned parameter. The definition of such a function is standard, and could be:
    void *Object::operator new[](size_t size, unsigned extra)
    {
        unsigned
            n = size / sizeof(Object);
        Object
            *op = ::new Object[n];

        for (unsigned idx = 0; idx < n; idx++)
            op[idx].value = extra;          // assume a member `value'

        return cp;
    }
To use this overloaded operator, only the extra parameter must be provided. It is given in a parameter list just after the name of the operator itself:
    Object
        *op = new(100) Object[12];
This results in an array of 12 Object objects, all having their value member set to 100.

Like operator new[] operator delete[] may be overloaded. To overload operator delete[] in a class Object the interface should contain the following lines, showing multiple forms of overloaded forms of operator delete[]:

    class Object
    {
        public:
            void operator delete[](void *p);                            
            void *operator delete[](void *p, size_t index);      
            void *operator delete[](void *p, int extra, bool yes); 
    };
The first form shows the basic form of operator delete[]. Its parameter is initialized to the address of a block of memory previously allocated by Object::new[]. These objects can be deleted by the global operator delete[] using the form . However, the compiler expects ::delete[] to receive a pointer to Objects, so a type cast is necessary:
::delete[] reinterpret_cast<Object *>(p)
; An example of an overloaded operator delete[] is:
    void Object::operator delete[](void *p)
    {
        cout << "operator delete[] for Objects called\n";
        ::delete  [] reinterpret_cast<Object *>(p);
    }
Having constructed the overloaded operator delete[], it will be used automatically in statements like:
        delete[] new Object[5];
Operator delete[] may be overloaded using extra parameters. However, if overloaded as
void *operator delete[](void *p, size_t size)

then size is automatically initialized to the size (in bytes) of the block of memory to which void *p points. If this form is defined, then the first form shoulld not be defined, to avoid ambiguity. An example of this form of operator delete[] is:

    void Object::operator delete[](void *p, unsigned size)
    {
        cout << "deleting " << size << " bytes\n";
        ::delete  [] reinterpret_cast<Object *>(p);
    }
If other parameters are defined, as in
void *operator delete[](void *p, int extra, bool yes)

an explicit argument list must be provided. With delete[], the argument list is specified behind the brackets:

    delete[](new Object[5], 100, false);

9.9: Function Objects

Function Objects are created by overloading the function call operator operator()(). By defining the function call operator an object may be used as a function, hence the term function objects.

Function objects play an important role in the generic algorithms and they can be used profitably as alternatives to using pointers to functions. The fact that they are important in the context of the generic algorithms constitutes some sort of a didactical dilemma: at this point it would have been nice if the generic algorithms would have been covered, but for the discussion of the generic algorithms knowledge of function objects is an advantage. This bootstrapping problem is solved in a well known way: by ignoring the dependency.

Function objects are objects for which the operator()() has been defined. Usually they are used in combination with generic algorithms, but they are also used in situations where otherwise pointers to functions would have been used. Another reason for using function objects is to support inline functions, which cannot be used in combination with pointers to functions.

Assume we have a class Person and an array of Person objects. Further assume that the array is not sorted. A well known procedure for finding a particular Person object in the array is to use the function lsearch(), which performs a lineair search in an array. A program fragment in which this function is used is, e.g.,

    Person
        &target = targetPerson(),   // determine the person we're looking for
        *pArray;
    unsigned
        n  = fillPerson(&pArray);

    cout << "The target person is";

    if (!lsearch(&target, pArray, &n, sizeof(Person), compareFunction))
        cout << " not";

    cout << "found\n";
The function targetPerson() is called to determine the person we're looking for, and the function fillPerson() is called to fill the array. Then lsearch() is used to locate the target person.

The comparison function must be available, as its address is one of the arguments of the lsearch() function. It could be something like:

    int compareFunction(Person const *p1, Person const *p2)
    {
        return (*p1 != *p2);    // lsearch() wants 0 for equal objects
    }
This, of course, assumes that the operator!=() has been overloaded in the class Person, as it is quite unlikely that a bytewise comparison will be appropriate here. But overloading operator!=() is no big deal, so let's assume that that operator is available as well.

With lsearch() (and friends, having parameters that are pointers to functions) an inline compare function cannot be used: as the address of the compare() function must be known to the lsearch() function. So, on the average n / 2 times at least the following actions take place:

When function objects are used a different picture emerges. Assume we have constructed a function PersonSearch(), having the following prototype (realize that this is not the preferred approach. Normally a generic algorithm will be preferred to a home-made function. But for now our PersonSearch() function is used to illustrate the use and implementation of a function object):
    Person const *PersonSearch(Person *base, size_t nmemb, 
                               Person const &target);
This function can be used as follows:
    Person
        &target = targetPerson(),
        *pArray;
    unsigned
        n = fillPerson(&pArray);

    cout << "The target person is";

    if (!PersonSearch(pArray, n, target))
        cout << " not";

    cout << "found\n";
So far, nothing much has been altered. We've replaced the call to lsearch() with a call to another function: PersonSearch(). Now we show what happens inside PersonSearch():
    Person const *PersonSearch(Person *base, size_t nmemb, 
                                Person const &target)
    {
        for (int idx = 0; idx < nmemb; ++idx)
            if (target(base[idx]))     
                return base + idx;   
        return 0;
    }
The implementation shows a plain linear search. However, in the for-loop the expression target(base[idx]) shows our target object used as a function object. Its implementation can be simple:
    int Person::operator()(Person const &other) const
    {
        return *this != other;
    }
Note the somewhat peculiar syntax: operator()(). The first set of parentheses define the particular operator that is overloaded: the function call operator. The second set of parentheses define the parameters that are required for this function. Operator()() appears in the class header file as:
    bool operator()(Person const &other) const;
Now, Person::operator()() is a simple function. It contains but one statement, so we could consider making it inline. Assuming that we do, than this is what happens when operator()() is called: Note that due to the fact that operator()() is an inline function, it is not actually called. Instead operator!=() is called immediately. Also note that the required stack operations are fairly modest.

So, function objects may be defined inline. This is not possible for functions that are called indirectly (i.e., using pointers to functions). Therefore, even if the function object needs to do very little work it has to be defined as an ordinary function if it is going to be called via pointers. The overhead of performing the indirect call may not outweight the advantage of the flexibility of calling functions indirectly. In these cases function objects that are defined as inline functions can result in an increase of efficiency of the program.

Finally, function objects may of course access their private data directly. A search algorithm where a compare function is used (as with lsearch()) the target and array elements are passed to the compare function using pointers, involving extra stack handling. When function objects are used, the target person doesn't vary within a single search task. Therefore, the target person could be passed to the constructor of the function object doing the comparison. This is in fact what happened in the expression target(base[idx]), where only one argument is passed to the operator()() member function of the target function object.

Actually, in the above example operator()() could have been avoided altogether in the above example. However, function objects play a central role in generic algorithms. In chapter 17 these generic algorithms are further discussed. Also in that chapter, the importance of function objects will be further emphasized when predefined function objects are discussed.

9.9.1: Constructing manipulators

In chapter 5 we saw constructions like cout << hex << 13 << endl to display the value 13 in hexadecimal format. One may wonder by what magic the hex manipulator accomplishes this. In this section the construction of manipulators like hex is covered.

Actually the construction of a manipulator is rather simple. To start, a definition of the manipulator is needed. Let's assume we want to create a manipulator w10 which will set the field width of the next field to be written to the ostream object to 10. This manipulator is constructed as a function. The w10 function will have to know about the ostream object in which the width must be set. By providing the function with a ostream & parameter, it obtains this knowledge. Now that the function knows about the ostream object we're referring to, it can set the width in that object.

Next, it must be possible to use the manipulator in an insertion sequence. This implies that the return value of the manipulator must be a reference to an ostream object also.

From the above considerations we're now able to construct our w10 function:

    #include <iosfwd>
    #include <iomanip>

    std::ostream &w10(std::ostream &str)
    {
        return str << std::setw(10);
    }
The w10 function can of course be used in a `stand alone' mode, but it can also be used as a manipulator. E.g.,
        #include <iostream>
        #include <iomanip>

        using namespace std;
    
        extern ostream &w10(ostream &str);
    
        int main()
        {
            w10(cout) << 3 << " ships sailed to America" << endl;
            cout << "And " << w10 << 3 << " more ships sailed too." << endl;
        }
The w10 function can be used as a manipulator because the class ostream has an overloaded operator<< accepting a pointer to a function expecting an ostream & and returning an ostream &. Its definition is:
    ostream& operator<<(ostream & (*func)(ostream &str)) 
    {
        return ((*func)(*this)); 
    }

The above procedure does not work for manipulators requiring arguments: it is of course possible to overload operator<< to accept an ostream reference and the address of a function expecting an ostream & and, e.g., an int, but while the address of such a function may be specified with the <<-operator, the arguments itself cannot be specified. So, one wonders how the following construction has been implemented:

cout << setprecision(3)

In this case the manipulator is defined as a macro. Macro's, however, are the realm of the preprocessor, and may easily suffer from unwanted side-effects. The following section introduces a way to implement manipulators requiring arguments without resorting to macros, but using anonymous objects.

9.9.1.1: Manipulators requiring arguments

Manipulators taking arguments are implemented as macros: they are handled by the preprocessor, and are not available beyond the preprocessing stage. The problem appears to be that you can't call a function in an insertion sequence: in a sequence of operator<<() calls the compiler will call the functions first, and then use their return values in the insertion sequence. That will invalidate the ordering of the arguments passed to your <<-operators.

So, one might consider constructing another overloaded operator<<() accepting the address of a function receiving not just the ostream reference, but a series of other arguments as well. The problem now is that it isn't clear how the function will receive its arguments: you can't just call it, since that produces the abovementioned problem, and you can't just pass its address in the insertion sequence, as you normally do with a manipulator....

However, there is a solution, based on the use of anonymous objects:

The class Manip could be, e.g.,
    class Manip
    {
        int d_value;

        friend ostream &operator<<(ostream &str, Manip const &x)
        {    
            return str << x.d_value;    
        }
        public:
            Manip(int value)
            :
                d_value(value)
            {}
    };
Now we're getting to where we want to be: by inserting anonymous Manip objects into an ostream the desired effect is reached: manipulators having (multiple) arguments. E.g.,
    int main()
    {
        cout << Manip(4) << " -- " << Manip(5) << " ++ " << Manip(6) << endl;
    }
    /*
        generated output:
    4 -- 5 ++ 6
    */
Note that in order to be able to insert an anonymous MultiMap object into the ostream, the MultiMap's operator<<() friend must define a Manip const & parameter.

9.10: Overloadable Operators

The following operators can be overloaded:
    +       -       *       /       %       ^       &       |
    ~       !       ,       =       <       >       <=      >=
    ++      --      <<      >>      ==      !=      &&      ||
    +=      -=      *=      /=      %=      ^=      &=      |=
    <<=     >>=     []      ()      ->      ->*     new     delete
Several of these operators may only be overloaded as member functions within a class. This holds true for the '=', the '[]', the '()' and the '->' operators. Consequently, it isn't possible to redefine, e.g., the assignment operator globally in such a way that it accepts a char const * as an lvalue and a String & as an rvalue. Fortunately, that isn't necessary either, as we have seen in section 9.3.