模板、实参推导与重载

来源:互联网 发布:淘宝买家好评率未知 编辑:程序博客网 时间:2024/06/09 20:20

以下的内容都是在VC++6.0上测试的。


一、如果非引用、非指针形参的差异只是一个const,那不能构成重构,如:

  (1) template <typename T>

       void func1(T t)

      {    ............     }

(2) template <typename T>

     void func1(const T t)

     {   ............    }

编译时,上面的(1)和(2)会报重复定义的错误:

 error C2995: 'func1' : template function has already been defined

如果改成引用或指针再编译就通过了。


二、引用、指针类型形参,重载函数的选择

  (3) template <typename T>

       void func2(const T &t)   

      {  ............  }

  (4)template <typename T>

      void func2(T &t)

      {..................}

  (5) template <typename T>

        void func3(const T *t)

        {  .............  }

  (6) template <typename T>

        void func3(T *t)

       {   .............  }

   

int i = 1;    int *pi = &i;

func2(i);    func3(pi);       这2个函数调用的都是没有const的函数版本,即(4)和(6)。


const int ci =2;

const int *pci = pi;

int *const cpi = pi;

func2(ci);    编译出错:

 error C2667: 'func2' : none of 2 overload have a best conversion
 error C2668: 'func2' : ambiguous call to overloaded function

func2(2);    编译出错,错误原因同上

func3(pci);  编译出错,错误原因同上

func3(cpi);  编译正确,调用的是(6)


三、实参推导的类型

     double x[20];

    func1(x);    推导出来的参数类型是double*,函数内sizeof(T)是4

    func2(x);   调用的是(4),推导出来的参数类型是double[20],函数内sizeof(T)是160

    func3(x);   调用的是(6),推导出来的参数类型是duble,T*是指向第一个元素的指针


double y[20];   double z[21];

   (7) template <typename T>

        void func4(T &t1, T &t2)  

      {.......................}

    func4(x, y);      推导出来T是double[20]

   func4(x, z);      编译错误, double[20]和double[21]冲突


(8) template <typename T>

     void func5(T t)

     {  ..............  }

(9) template <typename T>

    void func5(T &t)

    {  .............  }

(10) template <typename T>

        void func5(T *t)

       { .................... }

int i=1;  int *pi=&i;    double x[20];

func5(i);   编译出错,报有2个满足匹配条件的,T是int,应该是(8)、(9)

func5(pi);   编译出错,报有3个满足条件的错误。应该是(8)(9)的T推导成int *,(10)的T推导成int

func5(x);   编译出错,报有3个满足匹配的错误,(8)的T推导成double*,(9)的T推导成double[20],(10)的T推导成double


四、基类、派生类的类型转换

template<typename T>

class B

{  ............   }

template<typename T>

class D:public B<T>

{   .................  }


template<typename T>

void f(B<T> *c)  

{    .................  }


template<typename T>

void g(B<T> c)

{   ........................  }


    D<long> dl;     B<int>  bi;
f(&dl);
f(&bi);
g(bi);
g(dl);

这4个调用都可以编译通过并运行。


根据以上的几个实验,得到以下的一些结论:

1. 如果形参的声明是引用时,推导出来的类型是实参的实际类型,如前面的数组,是不会退化为指针的;

2. 如果实参是不带const修饰的,推导时也找不带const的函数版本;

3. 如果实参是带const的,如func2(ci),由于const int来推导(4)中的T,用int来推导(5)中的T,最后得到的都是func2(const T &),所以func2(ci)编译时会报有2个同等匹配的函数的错误。

4.形参可以是实参的基类类型,或者,实参是个指向X类的指针,形参是个指向X基类的指针。


C++Primier里的描述:

    一般而言,不会转换实参以匹配已有的实例化,相反,会产生新的实例。

    编译器只会执行两种转换:

1. const转换: 接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型和实参类型都忽略const,即无论传递const或非const对象给接受非引用的类型,都使用相同的实例化。

2. 数组或函数到指针的转换: 如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作执行其第一个元素的指针,函数实参当作指向函数类型的指针。