1. 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.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;
};
A conversion function takes the general form operator type()
.
A conversion function must be a member function. The function may not specify a return type, and the parameter list must be empty.
A class-type conversion may not be followed by another class-type conversion. If more than one class-type conversion is needed then the code is in error. 例如,A可以转换成B,B可以转换成C,那么在需要C的地方提供一个A是错误的。
2. Explicit Conversion Cast
int a = 10; |
3. Ambiguities When Two Classes Define Conversions
class Integral; |
In this case, we cannot use a cast to resolve the ambiguity, the cast itself could use either the conversion operation or the constructor. Instead, we would need to explicitly call the conversion operator or the constructor:compute(int_val.operator SmallInt()); // ok: use conversion operator
compute(SmallInt(int_val)); // ok: use SmallInt constructor
The best way to avoid ambiguities or surprises is to avoid writing pairs of classes where each offers an implicit conversion to the other.
4. Multiple Conversions and Overload Resolution
class SmallInt { |
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 solutions iscompute(static_cast<int>(si)); // ok: convert and call compute(int)
Let’s look at overload resolution when multiple conversion constructors exist:class SmallInt {
public:
SmallInt(int = 0);
};
class Integral {
public:
Integral(int = 0);
};
void manip(const Integral&);
void manip(const SmallInt&);
manip(10); // error: ambiguous
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)
Needing to use a constructor or a cast to convert an argument in a call to an overloaded function is a sign of bad design.
5. 标准转换优于类类型转换
class LongDouble |
调用void calc(int)所需要的转换:将实参dval由double类型转换为int
调用void calc(LongDouble)需要的转换:将实参dval由double类型转换为LongDouble
因为标准转换优于类类型转换,所以void calc(int)为最佳可行函数
6. 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 classes, that 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, then
(1) Do 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.
(2)Do not define a conversion to more than one arithmetic type. Let the standard conversions provide conversions to the other
arithmetic types.
The easiest rule of all: Avoid defining conversion functions and limit nonexplicit constructors to those that are “obviously right.”