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

本文深入探讨了C++中的模板参数推导(Template Argument Deduction)机制,包括推导过程、前后脉络、特殊推导情境、可接受的自变量转换等方面。通过示例解释了如何在函数模板调用时,编译器自动确定模板参数类型,并介绍了在不同情况下可能出现的匹配规则和错误情况。

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

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

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

1. 推导过程Dedcution Process,

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

template <typename T>

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>

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 <typename T> void f(T);   //P 为T

template <typename T> 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 <typename T>

   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 <typename T>

void f1(T*);

template <typename E, int N>

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

template <typename T1, typename T2, typenam T3>

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值