The first of these statements converts poppins weight to a double value, and then assignment converts the double value to type long. Similarly, the second statement converts poppins first to type int and then to long.
Like conversion constructors, conversion functions can be a mixed blessing. The problem with providing functions that make automatic, implicit conversions is that they may make conversions when you don’t expect them. Suppose, for example, that you happen to write the following code when you’re sleep deprived:
int ar[20];
...
Stonewt temp(14, 4);
...
int Temp = 1;
...
cout << ar[temp] << "!\n"; // used temp instead of Temp
Normally, you’d expect the compiler to catch a blunder such as using an object instead of an integer as an array index. But the Stonewt class defines an operator int(), so the Stonewt object temp is converted to the int 200 and be used as an array index. The moral is that often it’s best to use explicit conversions and exclude the possibility of implicit conversions. In C++98, the keyword explicit doesn’t work with conversion functions, but C++11 removes that limitation. So with C++11, you can declare a conversion operator as explicit:
class Stonewt
{
...
// conversion functions
explicit operator int() const;
explicit operator double() const;
};
With these declarations in place, you would use a type cast to invoke the operators.
Another approach is to replace a conversion function with a nonconversion function that does the same task—but only if called explicitly. That is, you can replace
Stonewt::operator int() { return int (pounds + 0.5); }
with
int Stonewt::Stone_to_Int() { return int (pounds + 0.5); }
This disallows the following:
int plb = poppins;
But if you really need a conversion, it allows the following:
int plb = poppins.Stone_to_Int();
Caution
You should use implicit conversion functions with care. Often a function that can only be invoked explicitly is the best choice.
In summary, then, C++ provides the following type conversions for classes:
• A class constructor that has but a single argument serves as an instruction for converting a value of the argument type to the class type. For example, the Stonewt class constructor with a type int argument is invoked automatically when you assign a type int value to a Stonewt object. However, using explicit in the constructor declaration eliminates implicit conversions and allows only explicit conversions.
• A special class member operator function called a
Conversions and Friends
Let’s bring addition to the Stonewt class. As mentioned in the discussion of the Time class, you can use either a member function or a friend function to overload addition. (To simplify matters, assume that no conversion functions of the operator double() form are defined.) You can implement addition with the following member function:
Stonewt Stonewt::operator+(const Stonewt & st) const
{
double pds = pounds + st.pounds;
Stonewt sum(pds);
return sum;
}
Or you can implement addition as a friend function this way:
Stonewt operator+(const Stonewt & st1, const Stonewt & st2)
{
double pds = st1.pounds + st2.pounds;
Stonewt sum(pds);
return sum;
}
Remember, you can provide the method definition or the friend definition but not both. Either form lets you do the following:
Stonewt jennySt(9, 12);
Stonewt bennySt(12, 8);
Stonewt total;
total = jennySt + bennySt;
Also given the Stonewt(double) constructor, each form lets you do the following:
Stonewt jennySt(9, 12);
double kennyD = 176.0;
Stonewt total;
total = jennySt + kennyD;
But only the friend function lets you do this:
Stonewt jennySt(9, 12);
double pennyD = 146.0;
Stonewt total;
total = pennyD + jennySt;
To see why, you can translate each addition into the corresponding function calls. First,
total = jennySt + bennySt;
becomes
total = jennySt.operator+(bennySt); // member function
or else
total = operator+(jennySt, bennySt); // friend function
In either case, the actual argument types match the formal arguments. Also the member function is invoked, as required, by a Stonewt object.
Next,
total = jennySt + kennyD;
becomes
total = jennySt.operator+(kennyD); // member function
or else
total = operator+(jennySt, kennyD); // friend function
Again, the member function is invoked, as required, by a Stonewt object. This time, in each case, one argument (kennyD) is type double, which invokes the Stonewt(double) constructor to convert the argument to a Stonewt object.