• C++模板编程(24)---模板自变量推导Template Argument Deduction


    如果在每一个function template调用语句中明确指定template argument,例如,

    concat(s,3), 程序代码会显得笨拙又难看。幸运的是C++编译器通常可以自动判定你所需要的template argument类型,这是透过要给名为template argument deduction模板自变量推导的强大机制完成的。

    本文详细解说template argument deduction的细节。很多推导规则所产生的结果都符合人们的直观认知。

    1. 推导过程Dedcution Process,

        编译器会比对调用语句内某自变量的类型和function template内对应的参数化类型,并试图总结出被推导的一个或多个参数的正确替代物。每一对自变量-参数独立分析。如果推导结束时结果有异常,则推导失败。考虑下面例子:

    template

    T const& max(T const& a, T const& b)

    {

            return a< b? b: a;

    }

    int g = max(1, 1.0);

    在这里,第一个call argument类型为int,因此max()的template parameter T被暂时推导为int。然而第二个call argument的类型为double,因此T应该为double:这与第一个推导结果冲突。注意是推导失败,不是程序非法:推导程序还是有可能成功找到另一个名为max的template(因为function template可以重载,就像常规functions那样)。

        即使所有的template parameters推导结果前后一致,但如果以自变量替换进去后导致函数声明的其余部分变为非法,推导过程还是有可能失败。例如:

    template

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

    {

            return a[i];

    }

    void f(int* p)

    {

            int x= at(p,7);

    }

    在这里T被推断为int*(T只在一个参数类型中出现,因此显然不会发生冲突)。然而,将返回类型T ::ElementT中的T替换为int* 显然是非法的,因此推导失败。

        我们探究自变量-参数匹配是如何进行的。今后的描述将出现符号A和P,意义如下:

    将类型A(由argument type导出)匹配至一个参数化类型P(有template parameter的声明导出); 

    如果参数被声明为以by reference 方式传递,则P表示被指涉reference类型,A为自变量类型;

    否则P表示声明之参数类型,A是个由array或function退化而成并去除外围之const 和volatile饰词的pointer类型。例如:

    template void f(T);   //P 为T

    template void g(T&); //P 仍为T

    double x[20];

    int const seven = 7;

    f(x);   //nonreferec parameter, T 为 double*

    g(x); // reference parameter, T 为double[20]

    f(seven); //nonreference parameter: T为 int

    g(seven);  //reference parameter: T 为int const

    f(7); //nonreference parameter: T 为int

    g(7); //reference paramter: T int =>错误,不能把7当作int& 传递

        对于调用语句f(x),x由array类型退化为double*类型,T被推导为该类型。调用语句f(seven)中,const饰词被卸除,从而T被推导为int。

        调用语句g(x)中,T被推导为double【20】(并没有发生类型退化);

    类似情况,g(seven)的自变量是一个int const类型的左值lvalue,此外,由于const和volatile饰词在匹配reference paramters 时不被卸除,因此T被推导为 int const。

       然而g(7)中T被推导为int(因为nonclass rvalue表达式不能含有带const或volatile饰词的类型),因此调用失败,因此自变量7不能传给一个int&类型的参数。

        当字符串字面常量string literal类型的自变量被绑定到reference parameter时,不会发生退化。重新考虑先前的max()template:

       template

       T const& max(T const & a, T const& b);

     很多人希望在max("Apple", "Pear");表达式中,T被推导为 const char*,然而,“Apple”的类型却是char const【6】,“Pear”的类型式char const【5】。array类型至pointer类型的退化过程并未发生(因为max的参数使用by reference传递方式),因此如果推导成功,T必须又是char【6】又是char【5】,那当然不可能。

    2.推导之前后脉络Deduced Contexts

    比T更复杂的参数化类型也可以被匹配到一个给定的自变量类型上。下面是一些基本例子:

    template

    void f1(T*);

    template

    void f2(E (&) [N]);

    template

    void f3(T1 (T2::*)(T3*)); //f3的参数是pointer-to-member type

                                 //f3的参数是个T2成员函数,该函数的返回类型为T1,接受一个T3*参数

    class S {

    public:

            void f(double *);

    };

    void g(int *** ppp)

    {

            bool b[42];

            f1(ppp);    // 推导结果:T为  int**

            f2(b);       //   推导结果:E  为 bool, N=42

            f3(&S::f);    //  推导结果:T1=void, T2=S, T3=double

    }

    3.特殊推导情境Special Decution Situations

    4. 可接受的自变量转型Allowable Argument Convertion

    5. 类模板参数Class Template Parameters

    6.默认的调用自变量Default Call Arguments

    7. 受限模板扩展技术restricted template expansion

    8. 小结 Summary

  • 相关阅读:
    C练题笔记之:Leetcode-654. 最大二叉树
    eyb后端项目搭建(一)
    利用spring写一个反向代理
    关于Pickle反序列化的研究总结和一道例题
    帮你快速理解一个复杂业务,系统思考入门
    Python升级之路( Lv15 ) 并发编程三剑客: 进程, 线程与协程
    macOS 13.6 及后续系统安装 Asahi Linux 将破坏引导
    R数据分析:扫盲贴,什么是多重插补
    机器学习介绍(上)
    Python中字典的使用方法
  • 原文地址:https://blog.csdn.net/zkmrobot/article/details/126114086