A conversion operator is a special kind of member function that converts a value of a class type to a value of some other type. A conversion function typically has the general form
operator type() const;
where type represents a type. Conversion operators can be defined for any type (other than void
) that can be a function return type (§ 6.1, p. 204). Conversions to an array or a function type are not permitted. Conversions to pointer types—both data and function pointers—and to reference types are allowed.
Conversion operators have no explicitly stated return type and no parameters, and they must be defined as member functions. Conversion operations ordinarily should not change the object they are converting. As a result, conversion operators usually should be defined as const
members.
A conversion function must be a member function, may not specify a return type, and must have an empty parameter list. The function usually should be const
.
As an example, we’ll define a small class that represents an integer in the range of 0 to 255:
class SmallInt {
public:
SmallInt(int i = 0): val(i)
{
if (i < 0 || i > 255)
throw std::out_of_range("Bad SmallInt value");
}
operator int() const { return val; }
private:
std::size_t val;
};
Our SmallInt
class defines conversions to and from its type. The constructor converts values of arithmetic type to a SmallInt
. The conversion operator converts SmallInt
objects to int
:
SmallInt si;
si = 4; // implicitly converts 4 to SmallInt then calls SmallInt::operator=
si + 3; // implicitly converts si to int followed by integer addition
Although the compiler will apply only one user-defined conversion at a time (§ 4.11.2, p. 162), an implicit user-defined conversion can be preceded or followed by a standard (built-in) conversion (§ 4.11.1, p. 159). As a result, we can pass any arithmetic type to the SmallInt
constructor. Similarly, we can use the converion operator to convert a SmallInt
to an int
and then convert the resulting int
value to another arithmetic type:
// the double argument is converted to int using the built-in conversion
SmallInt si = 3.14; // calls the SmallInt(int) constructor
// the SmallInt conversion operator converts si to int;
si + 3.14; // that int is converted to double using the built-in conversion
Because conversion operators are implicitly applied, there is no way to pass arguments to these functions. Hence, conversion operators may not be defined to take parameters. Although a conversion function does not specify a return type, each conversion function must return a value of its corresponding type:
class SmallInt;
operator int(SmallInt&); // error: nonmember
class SmallInt {
public:
int operator int() const; // error: return type
operator int(int = 0) const; // error: parameter list
operator int*() const { return 42; } // error: 42 is not a pointer
};
In practice, classes rarely provide conversion operators. Too often users are more likely to be surprised if a conversion happens automatically than to be helped by the existence of the conversion. However, there is one important exception to this rule of thumb: It is not uncommon for classes to define conversions to bool
.
Under earlier versions of the standard, classes that wanted to define a conversion to bool
faced a problem: Because bool
is an arithmetic type, a class-type object that is converted to bool
can be used in any context where an arithmetic type is expected. Such conversions can happen in surprising ways. In particular, if istream
had a conversion to bool
, the following code would compile:
int i = 42;
cin << i; // this code would be legal if the conversion to bool were not explicit!
This program attempts to use the output operator on an input stream. There is no <<
defined for istream
, so the code is almost surely in error. However, this code could use the bool
conversion operator to convert cin
to bool
. The resulting bool
value would then be promoted to int
and used as the left-hand operand to the built-in version of the left-shift operator. The promoted bool
value (either 1 or 0) would be shifted left 42 positions.
explicit
Conversion OperatorsTo prevent such problems, the new standard introduced explicit
conversion operators:
class SmallInt {
public:
// the compiler won't automatically apply this conversion
explicit operator int() const { return val; }
// other members as before
};
As with an explicit
constructor (§ 7.5.4, p. 296), the compiler won’t (generally) use an explicit
conversion operator for implicit conversions:
SmallInt si = 3; // ok: the SmallInt constructor is not explicit
si + 3; // error: implicit is conversion required, but operator int is explicit
static_cast<int>(si) + 3; // ok: explicitly request the conversion
If the conversion operator is explicit
, we can still do the conversion. However, with one exception, we must do so explicitly through a cast.
The exception is that the compiler will apply an explicit
conversion to an expression used as a condition. That is, an explicit
conversion will be used implicitly to convert an expression used as
• The condition of an if, while
, or do
statement
• The condition expression in a for
statement header
• An operand to the logical NOT (!
), OR (||
), or AND (&&
) operators
• The condition expression in a conditional (?:
) operator
bool
In earlier versions of the library, the IO types defined a conversion to void*
. They did so to avoid the kinds of problems illustrated above. Under the new standard, the IO library instead defines an explicit
conversion to bool
.
Whenever we use a stream object in a condition, we use the operator bool
that is defined for the IO types. For example,
while (std::cin >> value)
The condition in the while
executes the input operator, which reads into value
and returns cin
. To evaluate the condition, cin
is implicitly converted by the istream operator bool
conversion function. That function returns true
if the condition state of cin
is good
(§ 8.1.2, p. 312), and false
otherwise.
Conversion to bool
is usually intended for use in conditions. As a result, operator bool
ordinarily should be defined as explicit
.
Exercise 14.45: Write conversion operators to convert a Sales_data
to string
and to double
. What values do you think these operators should return?
Exercise 14.46: Explain whether defining these Sales_data
conversion operators is a good idea and whether they should be explicit
.
Exercise 14.47: Explain the difference between these two conversion operators:
struct Integral {
operator const int();
operator int() const;
};
Exercise 14.48: Determine whether the class you used in exercise 7.40 from § 7.5.1 (p. 291) should have a conversion to bool
. If so, explain why, and explain whether the operator should be explicit
. If not, explain why not.
Exercise 14.49: Regardless of whether it is a good idea to do so, define a conversion to bool
for the class from the previous exercise.