Borland Online And The Cobb Group Present:


November, 1994 - Vol. 1 No. 11

Applying user-defined conversions

In the article C++ Programming - An introduction to iterator classes in the July 1994 issue of Borland C++ Developer's Journal, you used a programming construct that works­­but I can't figure out how it does so. In the example code, you included the lines

while(FwdIterator)
  FwdIterator() = (value++) * 2;

The first line doesn't seem to use valid syntax. Apparently, the second line uses the overloaded function call operator, but how is the compiler able to accept the object FwdIterator as an argument to the while() expression?

Steve Robinson

Rockville, Maryland

Steve, in the forwardIterator class, we defined a special function called a conversion operator to convert forwardIterator objects to integer values. The primary purpose of this conversion is to let us test the iterator objects for an out-of-bounds condition when they iterate past the end of the array.

Unfortunately, in the article you mentioned, we mistakenly called the conversion operator a conversion constructor. This is a common mistake, since those functions do similar things. To clarify this issue, let's review the three ways you can specify when the compiler is able to convert a class object to another type.

Conversion by inheritance

The first way you can specify a valid conversion from one type to another is by using inheritance. By declaring an inheritance relationship between two classes, the derived class can have access to the copy constructor of the base class and use it to copy the base class subobject.

For example, consider the following class:

class RichDaddy
{ public:
   RichDaddy(RichDaddy& rd);
};

If you derive a new class by adding

class RichKid : public RichDaddy
{ // details
};

you'll be able to use a RichKid object any place you can use a RichDaddy object. This is because the compiler will be able to use the RichDaddy class's copy constructor to copy the RichDaddy subobject inside any RichKid object.

In addition, this is the only way for you to tell the compiler it can implicitly (without a cast) convert a pointer to a base type into a pointer to a derived type. However, this technique is limited­­you can't use it to convert a class object to a fundamental type (int, char, float, and so on) or vice-versa, because you can't derive new classes from or redefine the fundamental types.

Conversion constructors

The second technique you can use to allow conversion from one type to another is to define a conversion constructor. A conversion constructor simply accepts a single parameter of the desired type.

For example, if you add the conversion constructor

RichDaddy(int) { //details }

to the RichDaddy class, you can then supply an integer value in many places where the compiler expects a RichDaddy object.

Once again, you can use this technique only to create objects of user-defined classes. However, you can create user defined objects from a fundamental type.

Conversion operators/functions

The third kind of conversion you can define is called a conversion operator (some people call this a conversion function). Conversion operators are by far the most flexible way for you to change an object from a user-defined object into some other data type.

To define a conversion operator, you simply declare a member function using

operator type ();

where type can be any one of the following:

You'll never call a conversion operator directly. Instead, you can simply use an object from a user-defined class in places that would expect an object of the conversion type type. If the compiler finds an object in an expression where the compiler expects a different data type, the compiler will automatically call the appropriate conversion operator, if you've defined one.

Back at ITERATOR.CPP

In the example program (ITERATOR.CPP) from the article you mentioned, creating the operator int() conversion operator tells the compiler to turn the statement

while(FwdIterator)

into

while(FwdIterator.operator int())

because the while() expression expects an integral expression in the parentheses. The return value from the operator int() conversion operator function body will be 1 if the iterator is pointing to a location within the bounds of the array. If the iterator goes past the end of the array, the conversion operator calls reset() to reset the index to point to the beginning of the array, and then returns 0 to indicate the out-of-bounds condition.

Inside the parentheses of the while() expression, you must either provide an expression that reduces to an integral value or specify some form of conversion to an integral type. Since the conversion operator is available, the compiler will call it and use its return value as the argument for the while() expression.

Later in the main() function, we use the same technique to test both the forward and reverse iterators for an out-of-bounds condition, by writing

while(FwdIterator && RevIterator) {
  cout << FwdIterator() << "  ";
  cout << RevIterator() << endl; } 

These lines call the conversion operator for the FwdIterator and RevIterator objects and retrieve their integral out-of-bounds conditions (since the && operator expects integral operands). Then, the compiler can perform the logical AND operation on the resulting integer values.

Guidelines

As a general rule, use inheritance or conversion constructors to convert objects from one user-defined type to another user-defined type. To convert a fundamental type to a user-defined type, use a conversion constructor. Use conversion operators when you need to convert a user-defined type to a fundamental type or to any type of pointer.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.