ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## [$]一.模板实参推断 1. 函数实参的顶层 `const` 会被忽略,而底层 `const` 会保留。 #### 例1 函数实参为 `T` 时,传入 `const int` 的数据,则 `T=int` 。 函数实参为 `T &` 时,传入 `const int` 的左值,则 `T=const int` 。 函数实参为 `T &&` 时,传入 `const int` 的右值,则 `T=const int` 。 2. 模板推断中,类型转换是有限的: + 左值到右值的转换。例如,传入整型变量,编译器会推断为 `int` 而不是 `int &`,除非函数实参加了引用 `T &`。 + `const` 转换:非底层 `const` 转换成底层 `const` 。 + 数组转换成指向数组第一个元素的指针。 + 函数转换成函数指针。 其他的转换不会用于模板类型的函数参数,例如算术转换、派生类到基类的转换、用户定义的转换。 3. 相同的模板实参用于多个函数参数时, **实参必须严格一致** 。正常的函数参数不受影响。 4. 当用户指定显式的实参时,正常的类型转换便会应用于显式实参。 #### 例2 ```c++ template <typename T> T add(const T &t1,const T &t2) { return t1+t2; } ``` 函数调用: ```c++ add(298,700);//ok long l = 298; add(l,700);//wrong:编译器从第一个参数中推断出T=long,从第二个参数中推断出T=int。 add<int>(l,700);//ok:正常的类型转换会应用于显式实参 ``` 5. [11+]当参数被指定为右值引用时,可能会发生引用折叠,导致该参数既可以接受左值,也可以接受右值。 #### [11+]例3 函数实参为 `T &&` 时,传入 `int` 的纯右值,则 `T=int` 。 函数实参为 `T &&` 时,传入 `int` 的左值,则 `T=int &` ,但此时发生引用折叠, `&&&` 被折叠为 `&`,最终 `T &&` 是一个左值引用 `int &`。 函数实参为 `T &&` 时,传入将亡值 `int &&` ,则 `T=int &&` ,但此时发生引用折叠, `&&&&` 被折叠为 `&&`,最终 `T &&` 是一个右值引用 `int &&`。 ## [$]二.函数指针与实参推断 编译器会通过指针的类型来进行参数的推断,如果参数无法匹配或出现二义性,则编译器报错。 #### 例4 ```c++ //add的定义参见例2 typedef long (*Fp)(const long &,const long &); Fp f= add; long l = 298; f(l,700);//ok ``` ## [$]三.重载与模板 1. 候选的函数模板总是可行的,因为模板实参推断会排除任何不可行的模板。 2. 当多个函数提供了同样好的匹配:(越特例化越优先) + 如果同样好的函数中只有一个是非模板函数,则选择此函数。 + 如果同样好的函数中没有非模板函数,而有多个函数模板,且其中一个模板比其他模板更特例化,则选择此模板。 + 否则,此调用是二义性的。 #### 例5 假设有下面一组函数: ```c++ template <typename T> void print(const T &t); template <typename T> void print(T *p); void print(const string &s); ``` 1. 当通过以下方式调用时,匹配第一个版本。 ```c++ print(2333); ``` 2. 当通过以下方式调用时,生成两个可行的匹配: + `void print(const string &t);` + `void print(const string &s);` 正常的模板解析过程无法区分这两个函数。但是,根据重载函数模板的特殊规则,`const string &s` 不是模板类型,因此这里匹配第二个版本。 ```c++ string s = "2020"; print(s); ``` 3. 当通过以下方式调用时,生成两个可行的匹配: + `void print(const string *&t);` + `void print(string *p);` 而第一个版本需要非 `const` 到 `const` 的转换,因此这里匹配第二个版本。 ```c++ string s = "2020"; print(&s); ``` 4. 当通过以下方式调用时,生成了两个可行的匹配: + `void print(const string *&t);` + `void print(const string *p);` 正常的模板解析过程无法区分这两个函数。但是,根据重载函数模板的特殊规则,`const T &t` 适用于任意类型,而 `T *p` 适用于指针。也就是说,第二个版本更特例化,所以匹配到第二个版本。 ```c++ string s = "2020"; const string *ps = &s; print(ps); ``` 5. 当通过以下方式调用时,生成了三个可行的匹配: + `void print(const char (&t)[10]);` 这里参数是一个数组的引用。 + `void print(const char *p);` + `void print(const string *&s);` 第三个版本需要进行一次用户定义的类型转换,第二个版本需要进行数组到指针的转换,第一个版本不需要转换。但是数组到指针的转换被认为是精确匹配,所以只排除第三个版本。 与上一个例子一样,`const T &t` 适用于任意类型,而 `T *p` 适用于指针。也就是说,第二个版本更特例化,所以匹配到第二个版本。 ```c++ print("Hello"); ```