14.9.1. Conversion Operators

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.


Image Note

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.


Defining a Class with a Conversion Operator

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
};

Conversion Operators Can Yield Suprising Results

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 Operators
Image

To 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

Conversion to 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 good8.1.2, p. 312), and false otherwise.


Image Best Practices

Conversion to bool is usually intended for use in conditions. As a result, operator bool ordinarily should be defined as explicit.



Exercises Section 14.9.1

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.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.7.131