14.9. Conversions and Class Types
In Section 12.4.4 (p. 461) we saw that a nonexplicit constructor that can be called with one argument defines an implicit conversion. The compiler will use that conversion when an object of the argument type is supplied and an object of the class type is needed. Such constructors define conversions to the class type.
Exercises Section 14.8.3
Exercise 14.37:Using the library function objects and adaptors, define an object to:
- Find all values that are greater than 1024.Find all strings that are not equal to pooh.Multiply all values by 2.
14.9.1. Why Conversions Are Useful
Assume that we want to define a class, which we'll name SmallInt, to implement safe small integers. Our class will allow us to define objects that could hold the same range of values as an 8-bit unsigned charthat is, 0 to 255. This class would catch under- and overflow errors and so would be safer to use than a built-in unsigned char.We'd want our class to define all the same operations as are supported by an unsigned char. In particular, we'd want to define the five arithmetic operators (+, -, *, /, and %) and the corresponding compound-assignment operators; the four relational operators (<, <=, >, and >=); and the equality operators (== and !=). Evidently, we'd need to define 16 operators.
Supporting Mixed-Type Expressions
Moreover, we'd like to be able to use these operators in mixed-mode expressions. For example, it should be possible to add two SmallInt objects and also possible to add any of the arithmetic types to a SmallInt. We could come close by defining three instances for each operator:
Because there is a conversion to int from any of the arithmetic types, these three functions would cover our desire to support mixed mode use of SmallInt objects. However, this design only approximates the behavior of built-in integer arithmetic. It wouldn't properly handle mixed-mode operations for the floating-point types, nor would it properly support addition of long, unsigned int, or unsigned long. The problem is that this design converts all arithmetic types even those bigger than intto int and does an int addition.
int operator+(int, const SmallInt&);
int operator+(const SmallInt&, int);
SmallInt operator+(const SmallInt&, const SmallInt&);
Conversions Reduce the Number of Needed Operators
Even ignoring the issue of floating-point or large integral operands, if we implemented this design, we'd have to define 48 operators! Fortunately, C++ provides a mechanism by which a class can define its own conversions that can be applied to objects of its class type. For SmallInt, we could define a conversion from SmallInt to type int. If we define the conversion, then we won't need to define any of the arithmetic, relational, or equality operators. Given a conversion to int, a SmallInt object could be used anywhere an int could be used.If there were a conversion to int, then
would be resolved by
SmallInt si(3);
si + 3.14159; // convert si to int, then convert to double
- Converting si to an int.Converting the resulting int to double and adding it to the double literal constant 3.14159, yielding a double value.
14.9.2. Conversion Operators
A conversion operator is a special kind of class member function. It defines a conversion that converts a value of a class type to a value of some other type. A conversion operator is declared in the class body by specifying the keyword operator followed by the type that is the target type of the conversion:
A conversion function takes the general form
class SmallInt {
public:
SmallInt(int i = 0): val(i)
{ if (i < 0 || i > 255)
throw std::out_of_range("Bad SmallInt initializer");
}
operator int() const { return val; }
private:
std::size_t val;
};
where type represents the name of a built-in type, a class type, or a name defined by a typedef. Conversion functions can be defined for any type (other than void) that could be a function return type. In particular, conversions to an array or function type are not permitted. Conversions to pointer typesboth data and function pointersand to reference types are allowed.
operator type();

Although a conversion function does not specify a return type, each conversion function must explicitly return a value of the named type. For example, operator int returns an int; if we defined an operator Sales_item, it would return a Sales_item; and so on.
operator int(SmallInt &); // error: nonmember
class SmallInt {
public:
int operator int(); // error: return type
operator int(int = 0); // error: parameter list
// ...
};

Using a Class-Type Conversion
Once a conversion exists, the compiler will call it automatically (Section 5.12.1, p. 179) in the same places that a built-in conversion would be used:In expressions:
In conditions:
SmallInt si;
double dval;
si >= dval // si converted to int and then convert to double
When passing arguments to or returning values from a function:
if (si) // si converted to int and then convert to bool
As operands to overloaded operators:
int calc(int);
SmallInt si;
int i = calc(si); // convert si to int and call calc
In an explicit cast:
// convert si to int then call opeator<< on the int value
cout << si << endl;
int ival;
SmallInt si = 3.541; //
instruct compiler to cast si to int
ival = static_cast<int>(si) + 3;
Class-Type Conversions and Standard Conversions
When using a conversion function, the converted type need not exactly match the needed type. A class-type conversion can be followed by a standard conversion (Section 5.12.3, p. 181) if needed to obtain the desired type. For example, in the comparison between a SmallInt and a double
si is first converted from a SmallInt to an int, and then the int value is converted to double.
SmallInt si;
double dval;
si >= dval // si converted to int and then convert to double
Only One Class-Type Conversion May Be Applied

We could use an Integral where a SmallInt is needed, but not where an int is required:
// class to hold unsigned integral values
class Integral {
public:
Integral(int i = 0): val(i) { }
operator SmallInt() const { return val % 256; }
private:
std::size_t val;
};
When we create si, we use the SmallInt copy constructor. First int_val is converted to a SmallInt by invoking the Integral conversion operator to generate a temporary value of type SmallInt. The (synthesized) SmallInt copy constructor then uses that value to initialize si.The first call to calc is also okay: The argument si is automatically converted to int, and the int value is passed to the function.The second call is an error: There is no direct conversion from Integral to int. To get an int from an Integral would require two class-type conversions: first from Integral to SmallInt and then from SmallInt to int. However, the language allows only one class-type conversion, so the call is in error.
int calc(int);
Integral intVal;
SmallInt si(intVal); // ok: convert intVal to SmallInt and copy to si
int i = calc(si); // ok: convert si to int and call calc
int j = calc(intVal); // error: no conversion to int from Integral
Standard Conversions Can Precede a Class-Type Conversion
When using a constructor to perform an implicit conversion (Section 12.4.4, p. 462), the parameter type of the constructor need not exactly match the type supplied. For example, the following code invokes the constructor SmallInt(int) defined in class SmallInt to convert sobj to the type SmallInt:
If needed, a standard conversion sequence can be applied to an argument before a constructor is called to perform a class-type conversion. To call the function calc(), a standard conversion is applied to convert dobj from type double to type int. The SmallInt(int) constructor is then invoked to convert the result of the conversion to the type SmallInt.
void calc(SmallInt);
short sobj;
// sobj promoted from short to int
// that int converted to SmallInt through the SmallInt(int) constructor
calc(sobj);
Exercises Section 14.9.2
Exercise 14.40:Write operators that could convert a Sales_item to string and to double. What values do you think these operators should return? Do you think these conversions are a good idea? Explain why or why not.Exercise 14.41:Explain the difference between these two conversion operators:
Are either of these conversions too restricted? If so, how might you make the conversion more general?Section 14.2.1 (p. 515).Exercise 14.43:Explain what the bool conversion operator does. Is that the only possible meaning for this conversion for the CheckoutRecord type? Explain whether you think this conversion is a good use of a conversion operation.
class Integral {
public:
const int();
int() const;
};
14.9.3. Argument Matching and Conversions


Argument Matching and Multiple Conversion Operators
To illustrate how conversions on values of class type interact with function matching, we'll add two additional conversions to our SmallInt class. We'll add a second constructor that takes a double and also define a second conversion operator to convert SmallInt to double:
// unwise class definition:
// multiple constructors and conversion operators to and from the built-in types
// can lead to ambiguity problems
class SmallInt {
public:
// conversions to SmallInt from int and double
SmallInt(int = 0);
SmallInt(double);
// Conversions to int or double from SmallInt
// Usually it is unwise to define conversions to multiple arithmetic types
operator int() const { return val; }
operator double() const { return val; }
// ...
private:
std::size_t val;
};

Either conversion operator could be used in the call to compute:
void compute(int);
void fp_compute(double);
void extended_compute(long double);
SmallInt si;
compute(si); // SmallInt::operator int() const
fp_compute(si); // SmallInt::operator double() const
extended_compute(si); // error: ambiguous
- operator int generates an exact match to the parameter type.operator double followed by the standard conversion from double to int matches the parameter type.
Argument Matching and Conversions by Constructors
Just as there might be two conversion operators, there can also be two constructors that might be applied to convert a value to the target type of a conversion.Consider the manip function, which takes an argument of type SmallInt:
In the first call, we could use either of the SmallInt constructors to convert d to a value of type SmallInt. The int constructor requires a standard conversion on d, whereas the double constructor is an exact match. Because an exact match is better than a standard conversion, the constructor SmallInt(double) is used for the conversion.In the second call, the reverse is true. The SmallInt(int) constructor provides an exact matchno additional conversion is needed. To call the SmallInt constructor that takes a double would require that i first be converted to double. For this call, the int constructor would be used to convert the argument.The third call is ambiguous. Neither constructor is an exact match for long. Each would require that the argument be converted before using the constructor:
void manip(const SmallInt &);
double d; int i; long l;
manip(d); // ok: use SmallInt(double) to convert the argument
manip(i); // ok: use SmallInt(int) to convert the argument
manip(l); // error: ambiguous
- standard conversion (long to double) followed by SmallInt(double)standard conversion (long to int) followed by SmallInt(int)

Ambiguities When Two Classes Define Conversions
When two classes define conversions to each other, ambiguities are likely:
The argument int_val can be converted to a SmallInt in two different ways. The compiler could use the SmallInt constructor that takes an Integral object or it could use the Integral conversion operation that converts an Integral to a SmallInt. Because these two functions are equally good, the call is in error.In this case, we cannot use a cast to resolve the ambiguitythe cast itself could use either the conversion operation or the constructor. Instead, we would need to explicitly call the conversion operator or the constructor:
class Integral;
class SmallInt {
public:
SmallInt(Integral); // convert from Integral to SmallInt
// ...
};
class Integral {
public:
operator SmallInt() const; // convert from SmallInt to Integral
// ...
};
void compute(SmallInt);
Integral int_val;
compute(int_val); // error: ambiguous
Moreover, conversions that we might think would be ambiguous can be legal for what seem like trivial reasons. For example, our SmallInt class constructor copies its Integral argument. If we change the constructor so that it takes a reference to const Integral
compute(int_val.operator SmallInt()); // ok: use conversion operator
compute(SmallInt(int_val)); // ok: use SmallInt constructor
our call to compute(int_val) is no longer ambiguous! The reason is that using the SmallInt constructor requires binding a reference to int_val, whereas using class Integral's conversion operator avoids this extra step. This small difference is enough to tip the balance in favor of using the conversion operator.
class SmallInt {
public:
SmallInt(const Integral&);
};

Caution: Avoid Overuse of Conversion Functions
As with using overloaded operators, judicious use of conversion operators can greatly simplify the job of a class designer and make using a class easier. However, there are two potential pitfalls: Defining too many conversion operators can lead to ambiguous code, and some conversions can be confusing rather than helpful.The best way to avoid ambiguities is to ensure that there is at most one way to convert one type to another. The best way to do that is to limit the number of conversion operators. In particular there should be only one conversion to a built-in type.Conversion operators can be misleading when they are used where there is no obvious single mapping between the class type and the conversion type. In such cases, providing a conversion function may be confusing to the user of the class.As an example, if we had a class that represented a Date, we might think it would be a good idea to provide a conversion from Date to int. However, what value should the conversion function return? The function might return the julian date, which is the sequence number of the current date starting from 0 as January 1. But should the year precede the day or follow it? That is, would January 31, 1986 be represented as 1986031 or 311986? Alternatively, the conversion operator might return an int representing the day count since some epoch point. The counter might count days since January 1, 1971 or some other starting point.The problem is that whatever choice is made, the use of Date objects will be ambiguous because there is no single one-to-one mapping between an object of type Date and a value of type int. In such cases, it is better not to define the conversion operator. Instead, the class ought to define one or more ordinary members to extract the information in these various forms.
14.9.4. Overload Resolution and Class Arguments
As we have just seen, the compiler automatically applies a class conversion operator or constructor when needed to convert an argument to a function. Class Section 7.8.2, p. 269) consists of three steps:
1. Determine the set of candidate functions: These are the functions with the same name as the function being called.2. Select the viable functions: These are the candidate functions for which the number and type of the function's parameters match the arguments in the call. When selecting the viable functions, the compiler also determines which conversion operations, if any, are needed to match each parameter.3. The best match function is selected. To determine the best match, the type conversions needed to convert argument(s) to the type of the corresponding parameter(s) are ranked. For arguments and parameters of class type, the set of possible conversions includes class-type conversions.
Standard Conversions Following Conversion Operator
Which function is the best match can depend on whether one or more class-type conversions are involved in matching different functions.

Assuming we use our original SmallInt class that only defines one conversion operatorthe conversion to intthen if we pass a SmallInt to compute, the call is matched to the version of compute that takes an int.All three compute functions are viable:compute(int) is viable because SmallInt has a conversion to int. That conversion is an exact match for the parameter.compute(double) and compute(long double) are also viable, by using the conversion to int followed by the appropriate standard conversion to either double or long double.
void compute(int);
void compute(double);
void compute(long double);
Because all three functions would be matched using the same class-type conversion, the rank of the standard conversion, if any, is used to determine the best match. Because an exact match is better than a standard conversion, the function compute(int) is chosen as the best viable function.

Multiple Conversions and Overload Resolution
We can now see one reason why adding a conversion to double is a bad idea. If we use the revised SmallInt class that defines conversions to both int and double, then calling compute on a SmallInt value is ambiguous:
In this case we could use the operator int to convert si and call the version of compute that takes an int. Or we could use operator double to convert si and call compute(double).The compiler will not attempt to distinguish between two different class-type conversions. In particular, even if one of the calls required a standard conversion following the class-type conversion and the other were an exact match, the compiler would still flag the call as an error.
class SmallInt {
public:
// Conversions to int or double from SmallInt
// Usually it is unwise to define conversions to multiple arithmetic types
operator int() const { return val; }
operator double() const { return val; }
// ...
private:
std::size_t val;
};
void compute(int);
void compute(double);
void compute(long double);
SmallInt si;
compute(si); // error: ambiguous
Explicit Constructor Call to Disambiguate
A programmer who is faced with an ambiguous conversion can use a cast to indicate explicitly which conversion operation to apply:
This call is now legal because it explicitly says which conversion operation to apply to the argument. The type of the argument is forced to int by the cast. That type exactly matches the parameter of the first version of compute that takes an int.
void compute(int);
void compute(double);
SmallInt si;
compute(static_cast<int>(si)); // ok: convert and call compute(int)
Standard Conversions and Constructors
Let's look at overload resolution when multiple conversion constructors exist:
The problem is that both classes, Integral and SmallInt, provide constructors that take an int. Either constructor could be used to match a version of manip. Hence, the call is ambiguous: It could mean convert the int to Integral and call the first version of manip, or it could mean convert the int to a SmallInt and call the second version.This call would be ambiguous even if one of the classes defined a constructor that required a standard conversion for the argument. For example, if SmallInt defined a constructor that took a short instead of an int, the call manip(10) would require a standard conversion from int to short before using that constructor. The fact that one call requires a standard conversion and the other does not is immaterial when selecting among overloaded versions of a call. The compiler will not prefer the direct constructor; the call would still be ambiguous.
class SmallInt {
public:
SmallInt(int = 0);
};
class Integral {
public:
Integral(int = 0);
};
void manip(const Integral&);
void manip(const SmallInt&);
manip(10); // error: ambiguous
Explicit Constructor Call to Disambiguate
The caller can disambiguate by explicitly constructing a value of the desired type:
manip(SmallInt(10)); // ok: call manip(SmallInt)
manip(Integral(10)); // ok: call manip(Integral)

14.9.5. Overloading, Conversions, and Operators
Overloaded operators are overloaded functions. The same process that is used to resolve a call to an overloaded function is used to determine which operator built-in or class-typeto apply to a given expression. Given code such as
ClassX sc;
int iobj = sc + 3;
Exercises Section 14.9.4
Exercise 14.44:Show the possible class-type conversion sequences for each of the following initializations. What is the outcome of each initialization?
Exercise 14.45:Which calc() function, if any, is selected as the best viable function for the following call? Show the conversion sequences needed to call each function and explain why the best viable function is selected.
class LongDouble {
operator double();
operator float();
};
LongDouble ldObj;
(a) int ex1 = ldObj; (b) float ex2 = ldObj;
class LongDouble {
public
LongDouble(double);
// ...
};
void calc(int);
void calc(LongDouble);
double dval;
calc(dval); // which function?
there are four possibilities:
Overload Resolution and Operators
Section 7.8.2, p. 269) for operators follows the usual three-step process:
1. Select the candidate functions.2. Select the viable functions including identifying potential conversions sequences for each argument.3. Select the best match function.
Candidate Functions for Operators
As usual, the set of candidate functions consists of all functions that have the name of the function being used, and that are visible from the place of the call. In the case of an operator used in an expression, the candidate functions include the built-in versions of the operator along with all the ordinary nonmember versions of that operator. In addition, if the left-hand operand has class type, then the candidate set will contain the overloaded versions of the operator, if any, defined by that class.

Caution: Conversions and Operators
Correctly designing the overloaded operators, conversion constructors, and conversion functions for a class requires some care. In particular, ambiguities are easy to generate if a class defines both conversion operators and overloaded operators. A few rules of thumb can be helpful:
- Never define mutually converting classesthat is, if class Foo has a constructor that takes an object of class Bar, do not give class Bar a conversion operator to type Foo.Avoid conversions to the built-in arithmetic types. In particular, if you do define a conversion to an arithmetic type, thenDo not define overloaded versions of the operators that take arithmetic types. If users need to use these operators, the conversion operation will convert objects of your type, and then the built-in operators can be used.Do not define a conversion to more than one arithmetic type. Let the standard conversions provide conversions to the other arithmetic types.
Conversions Can Cause Ambiguity with Built-In Operators
Let's extend our SmallInt class once more. This time, in addition to a conversion operator to int and a constructor from int, we'll give our class an overloaded addition operator:
Now we could use this class to add two SmallInts, but we will run into ambiguity problems if we attempt to perform mixed-mode arithmetic:
class SmallInt {
public:
SmallInt(int = 0); // convert from int to SmallInt
// conversion to int from SmallInt
operator int() const { return val; }
// arithmetic operators
friend SmallInt
operator+(const SmallInt&, const SmallInt&);
private:
std::size_t val;
};
The first addition uses the overloaded version of + that takes two SmallInt values. The second addition is ambiguous. The problem is that we could convert 0 to a SmallInt and use the SmallInt version of +, or we could convert s3 to int and use the built-in addition operator on ints.
SmallInt s1, s2;
SmallInt s3 = s1 + s2; // ok: uses overloaded operator+
int i = s3 + 0; // error: ambiguous

Viable Operator Functions and Conversions
We can understand the behavior of these two calls by listing the viable functions for each call. In the first call, there are two viable addition operators:operator+(const SmallInt&, const SmallInt&)The built-in operator+(int, int)
The first addition requires no conversions on either argument s1 and s2 match exactly the types of the parameters. Using the built-in addition operator for this addition would require conversions on both arguments. Hence, the overloaded operator is a better match for both arguments and is the one that is called. For the second addition
the same two functions are viable. In this case, the overloaded version of + matches the first argument exactly, but the built-in version is an exact match for the second argument. The first viable function is better for the left operand, whereas the second viable function is better for the right operand. The call is flagged as ambiguous because no best viable function can be found.
int i = s3 + 0; // error: ambiguous
Exercises Section 14.9.5
Exercise 14.46:Which operator+, if any, is selected as the best viable function for the addition operation in main? List the candidate functions, the viable functions, and the type conversions on the arguments for each viable function.
class Complex {
Complex(double);
// ...
};
class LongDouble {
friend LongDouble operator+(LongDouble&, int);
public:
LongDouble(int);
operator double();
LongDouble operator+(const complex &);
// ...
};
LongDouble operator+(const LongDouble &, double);
LongDouble ld(16.08);
double res = ld + 15.05; // which operator+ ?