前言
以下内容根据Effective Modern C++ 条款1再结合自身理解而来(理解不对的还望指正,谢谢)
感谢作者 Scott Meyers带来这么好的书籍
类型推导不仅仅依赖 expr还依赖 ParamType
```
template<typename T>
void f(ParamType param);、
f(expr);
1. 编译期间将推导出两个类型: T, ParamType 分别对应的类型。
2. 往往两个型别不一样。因为ParamType中常含有修饰词: const, &, && 等等
template <typename T>
void f(const T& param);
1. f(1); : T int, ParamType(const T&): const int &
2. 也许你会认为 T的类型就是expr的类型,但不总成立,因为T的类型不仅仅依赖 expr, 还依赖 ParamType的形式
```
ParamType 具有指针或引用型别 (但不是万能引用&&)
- 如果expr具有引用型别,忽略它
- 然后再通过expr与ParamType的型别来确定T的型别
1) 引用
template <typename T>
void f(T& param);
ex:
int a = 10; // a 的型别是 int
const int b = a; // b 的型别是 const int
const int &c = a; // c 的型别是 const int &
f(a); // ParamType型别: int &; T的型别: int
f(b); // ParamType型别:const int &, T的型别: const int
f(c); // expr有引用,忽略, ParamType型别:const int &, T的型别: const int
:请思考以上三种调用,在void f(T& param) 定义中修改param会发生什么,如果可以的话。
: 新增
template <typename T>
void f(const T& param);
f(a); // T的型别: int; ParamType型别: int &
f(b); // T的型别: int; ParamType型别: const int &
f(c); // T的型别: int; ParamType型别: const int &
: 新增 void f(const T& param); 为什么推导结果如上?(最优适配)
2) 指针 (忽略之前的模板函数)
template <typename T>
void f(T* param);
int a = 10; // 同上
int *px = &a;
const int *pa = &a;
f(&a); // T的型别: int; ParamType型别: int *
f(px); // T的型别: int; ParamType型别: int *
f(pa); // T的型别: int; ParamType型别: const int *
ParamType 是一个万能引用(&&) - 区分左右值!!!
- 若 expr 是左值, T和ParamType均被推导为左值引用(废话?)
- 结果存在双重奇特: ① T 唯一被推导出引用的情形; ② 尽管声明时使用的是右值引用语法,但他的型别推导却是左值引用。
- 若 expr 是右值, 则按照指针或引用(非万能引用)的方式推导
template <typename T>
void f(T&& param);
int a = 8; // 同前
const int b = a; // 同前
const int &c = a; // 同前
f(a); // a是==左==值 - T的型别: int &; ParamType型别: const int &
f(b); // b是==左==值 - T的型别: const int &; ParamType型别: const int &
f(c); // c是==左==值 - T的型别: const int &; ParamType型别: const int &
f(8); // 8是==右==值 - T的型别: int; ParamType型别: int &&
ParamType 既非指针也非引用 - 值
该情况下意味无论传入什么,param仅仅是它的一个副本(值同,地址不同, 全新的对象)
- 一如之前,若expr具有引用型别,忽略引用部分
- 忽略引用性后, 若expr是 const 或 volatile (一般用于设备驱动)对象同样忽略
template <typename T>
void f(T param);
ex:
int a = 8; // 同前
const int b = a; // 同前
const int &c = a; // 同前
f(a); // ParamType与T的型别均是: int
f(b); // ParamType与T的型别均是: int
f(c); // ParamType与T的型别均是: int
:请考虑 param 的地址为什么一样?
:const 对象的 const 指针又会怎么样?!!!!! [欢迎讨论]
const int * const pt = &a;
f(pt); // ParamType与T的型别均是: const int *
数组实参 - 边缘情况
数组引用 - What? - 后期会继续研读并完善
尽管函数无法声明真正的的数组型别的形参,但是可以声明形参为数组的引用。
```
template <typename T>
void f(T ¶m); // 没错,老伙计
: 这种情况下会被推导出真正的数组型别形参,这个型别也会包含数组的长度
```
1) 先看一个又意思的模块
```
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept
{
return N;
}
constexpr: 使得返回值在编译期间就可以使用,从而声明另外一个数组时,可以指定大小与另外一个数组的相同。
const char name[] = "Tiany";
char aliasName[arraySize(name)]; // 候老提议使用 std::array 吧
noexcept: 帮助编译器生成更好的目标代码
```
可退化成指针之物 - 后期会继续研读并完善
针对数组向指针退化的一切讨论均适用函数向函数指针的退化
template <typename T>
void f1(T param);
template <typename T>
void f2(T& param);
void func(int, double);
f1(func); // T 和 ParamType的型别: void (*)(int, double);
f2(func); // T 和 ParamType的型别: void (&)(int, double);