C++ Primer 学习笔记_63_重载操作符与转换 --转换与类类型(二)

2014-11-24 12:41:28 · 作者: · 浏览: 2
uble类型转换为int类型,然后调用构造函数SmallInt(int)将转换结果转换为SmallInt类型。

    void calc(SmallInt);
    double dval;
    calc(dval);
    //作用等同于
    //calc(static_cast
  
   (dval));

  


//P457 习题14.40
class Sales_item
{
public:
    Sales_item(const std::string &book = ""):
        isbn(book), units_sold(0), revenue(0.0) {}

    /**
    *其实定义string和double的转换操作符并不是一个好办法
    *因为一般不必在需要string和double的地方使用Sales_item对象
    */

    operator string () const
    {
        return isbn;
    }
    operator double () const
    {
        return revenue;
    }
//As before...

private:
    std::string isbn;
    unsigned units_sold;
    double revenue;

};

//习题14.42
class CheckoutRecord
{
public:
    typedef unsigned Date;

    operator bool () const
    {
        return wait_list.empty();
    }
    //As Before...

private:
    //As Before...
    vector< pair
  
    * > wait_list;
};

  


三、实参匹配和转换

虽然类类型转换可能是实现和使用类的一个好处,但类类型转换也可能是编译时错误的一大来源!当从一个类型转换到另一个类型有多种方式时,如果有几个类类型转换可以使用,编译器必须决定对给定表达式使用哪一个

【小心地雷】

如果小心使用,类类型转换可以大大简化类代码和用户代码。如果使用得太过自由随意,类类型转换会产生令人迷惑的编译时错误,这些错误难以理解而且难以避免!

1、实参匹配和多个转换操作符

class SmallInt
{
public:
    //从int/double转换到SmallInt
    SmallInt(int = 0);
    SmallInt(double);

    //从SmallInt转换到int/double
    operator int() const
    {
        return val;
    }
    operator double () const
    {
        return val;
    }

private:
    std::size_t val;
};

【小心地雷】

一般而言,给出一个类两个内置类型之间的转换是不好的做法!

考虑最简单的调用非重载函数的情况:

    void compute(int);
    void fp_compute(double);
    void extended_compute(long double);

    SmallInt si;
    compute(si);            //OK
    fp_compute(si);         //OK
    extended_compute(si);   //Error

在这段程序中,任一转换操作符都可用于compute调用中:

1)operatorint 产生对形参类型的完全匹配。

2)首先调用operatordouble 进行转换,后跟从double到 int的标准转换与形参类型匹配。

由于完全匹配转换比需要标准转换的其他转换更好,因此,第一个转换序列更好,选择转换函数SmallInt::operatorint()来转换实参。

在第二个调用中,可用任一转换调用fp_compute。但是,到double的转换是一个完全匹配,不需要额外的标准转换。

最后一个对extended_compute的调用有二义性。可以使用任一转换函数,但每个都必须跟上一个标准转换来获得longdouble,因此,没有一个转换比其他的更好,调用具有二义性

【小结】

如果两个转换操作符都可用在一个调用中,而且在转换函数之后存在标准转换,则根据该标准转换的类别选择最佳匹配!

2、实参匹配和构造函数转换

正如可能存在两个转换操作符,也可能存在两个构造函数可以用来将一个值转换为目标类型。

    void manip(const SmallInt &);

    double d;
    int i;
    long l;
    manip(d);   //OK
    manip(i);   //OK
    manip(l);   //Error

在第一个调用中,可以用任一构造函数将d转换为 SmallInt类型的值:int构造函数需要对d的标准转换,double构造函数完全匹配。因为完全匹配比标准转换更好,所以用构造函数SmallInt(double)进行转换。

在第二个调用中,构造函数SmallInt(int)提供完全匹配,调用接受一个double参数的 SmallInt构造函数需要首先将i转换为 double类型。对于这个调用,则编译器更喜欢使用int构造函数转换实参。

第三个调用具有二义性。没有构造函数完全匹配于long。使用每一个构造函数之前都需要对实参进行转换:

1)标准转换(从long到 double)后跟SmallInt(double)。

2)标准转换(从long到 int)后跟SmallInt(int)。

这些转换序列是不能区别的,所以该调用具有二义性。

【小结】

当两个构造函数定义的转换都可以使用时,如果存在构造函数实参所需的标准转换,就用标准转换的类型选择最佳匹配

3、当两个类定义了转换时的二义性

当两个类定义了相互转换时,很可能存在二义性:

class Integral;
class SmallInt
{
public:
    SmallInt(Integral);
    //...
};

class Integral
{
public:
    operator SmallInt() const;
    //...
};

    void compute(SmallInt);

    Integral int_val;
    /**
    *Error:调用有二义性
    *但是有些编译器还是检测不出来的,比如我所测试的g++编译器
    */
    compute(int_val);

实参int_val可以用两种不同的方式转换成为SmallInt对象,编译器可以接受SmallInt对象的构造函数(此处中文版翻译有误,原文为:...Thecompiler could use the SmallInt constructor that takes and Integralobject or it could use the Integral conversion operation thatconverts an Integral to aSmallInt...,所以应该翻译为SmallInt对象的构造函数,而不是Integral对象的构造函数),也可以使用Integral对象转换为SmallInt对象