zoukankan      html  css  js  c++  java
  • 《c++ templates》学习笔记(8)——第十一章 模板实参演绎

     

    1       第十一章 模板实参演绎

    1.1    演绎的过程

    每个实参-参数对的分析都是独立的;因此,如果最后得出的结果是矛盾的,那么演绎的过程将失败。

    我们来看个例子:

    template<typename T>

    typename T::ElementT at(T const& a, int i)

    {

         return a[i];

    }

     

     

    void g1(int* p)

    {

         int x = at(p, 2);

    }

     

    void g2(int* p)

    {

         int x = at<int*>(p, 2);

    }

    在上面的例子中,g1g2都会报错,但是错误的信息会有所不同。

    g1报错是因为演绎失败,g2报错是因为指定的类型是个无效类型。S

    下面给出实参-参数匹配的规则:匹配类型A(来自实参的类型)和参数化类型P(来自参数的声明)。如果被声明的参数是一个引用声明(即T&),那么P就是所引用的类型(即T),而A仍然是实参的类型。否则的话,P就是所声明的参数类型,而A则是实参的类型;如果这个实参的类型是数组或者函数类型,那么还会发生decay转型,转换为对应的指针类型,同时还会ihulue高层次的constvolatile限定符

     

    这里要记住的一点是:引用参数是不会进行decay的。

    1.2    节接受的实参转型

    在找不到匹配实参的时候,有一些转型是允许的;

    l         从有const volatile限定符到没有;

    l         从没有限定符到有constvolatile限定符;

    l         当演绎的类型不涉及到转型运算符的时候,被替换的P类型可以是A类型的基类;或者当A是指针类型时,P可以是一个指针类型,它所指向的类型是A所指向的类型的基类。

    这里要重点说明一下第三点。例如:

    template<typename T>

    class B{

    public:

         virtual void f(){

             std::cout<<"B::f()"<<std::endl;

         }

    };

     

    template<typename T>

    class D: public B<T>{

    public:

         virtual void f(){

             std::cout<<"D::f()"<<std::endl;

         }

    };

     

    template<typename T>

    void f(B<T>* b)

    {

         b->f();

    }

    template<typename T>

    void f(B<T> b)

    {

         b.f();

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

         D<int> d;

         f(&d);//1, void f(B<T>* b)

         f(d); //2, void f(B<T> b)

         return 0;

    }

    在上面的例子中,2值得我们关注,这种传值的方式也可以实现子类到基类的匹配还是想出不来的,不过这里经过验证是如此,我们只需记住就好。

    如果对f再加上下面这个重载。

     

    template<typename T>

    void f(D<T> d)

    {

         d.f();

    }

    那么,在上面的2处调用的就是这个版本,而不是void f(B<T> b)版。

    原来我以为上面的规则是模板适用,经过实验发现,其实是通用的:

    class BB{};

     

    class DD:public BB{};

     

    void f(BB b)

    {

         std::cout<<"void f(BB b)"<<std::endl;

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

         DD dd;

         f(dd);//调用的是void f(BB b)

         return 0;

    }

    但是如果将上面的代码改为:

    class BB{};

     

    class DD:public BB{};

     

    template<typename T>

    void f(T a)

    {

         std::cout<<"void f(T a)"<<std::endl;

    }

     

    void f(BB b)

    {

         std::cout<<"void f(BB b)"<<std::endl;

    }

    int _tmain(int argc, _TCHAR* argv[])

    {

         DD dd;

         f(dd);//调用的是void f(T a)

         return 0;

    }

    此处调用的就是模板,这说明,只有在是在找不到匹配的类型时,c++编译器才会去转型以适应参数调用。

    1.3    缺省实参调用

    对于缺省调用实参而言,及时不是依赖型的,也不能用于演绎模板实参。来看例子:

    template<typename T>

    void h(T x = 42)

    {}

    int _tmain(int argc, _TCHAR* argv[])

    {

         h<int>();//ok

         h(); //error,42不能用来演绎T

         return 0;

    }

  • 相关阅读:
    BZOJ 1433 && Luogu P2055 [ZJOI2009]假期的宿舍 匈牙利算法
    BZOJ 1123 && Luogu P3469 [POI2008]BLO-Blockade 割点+乘法原理
    POJ3694 Network 边双缩点+LCA+并查集
    luogu P5142 区间方差 十分优美的线段树
    luogu P2709 小B的询问 最简单的莫队
    luogu P2731 骑马修栅栏 Riding the Fences
    TYVJ P2032 「Poetize9」升降梯上 spfa最短路
    51nod 1515 明辨是非 并查集+set维护相等与不等关系
    BZOJ 1260: [CQOI2007]涂色paint 区间DP
    luogu P4145 上帝造题的七分钟2 / 花神游历各国 维护区间和&&区间开根号
  • 原文地址:https://www.cnblogs.com/strinkbug/p/1342699.html
Copyright © 2011-2022 走看看