c++ primer笔记4

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;
SmallInt si(18);
int sum1 = si + a; // ok
int sum2 = static_cast<int>(si) + a; // ok, instruct compiler to cast si to int

3. Ambiguities When Two Classes Define Conversions

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

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 {
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

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 is

compute(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 
{

public:
LongDouble(double);
// ...
};

void calc(int);
void calc(LongDouble);
double dval;
calc(dval); // which function?

调用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:

  1. 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.
  2. 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.”