【Effective modern C++ 提炼】《类型推导》- Item1:《模板类型推导规则》

template<typename T>
void f(ParamType param);

f(expr);    // 调用该模板函数

编译期间,编译器会通过expr推导2种类型(T 跟 ParamType);

类型经常是不同的,因为ParamType包括了很多修饰符(e.g. const or reference);

如:

template<typename T>
void f(const T& param); // ParamType is const T&

int x = 0;
f(x);           // call f with an int

那么,T被推导成int类型,而ParamType被推导成const int&类型;

但T的类型推导,不仅仅取决于expr,也取决于ParamType的型式;

【下面的3个案例,讲解了这种特性】:

template<typename T>
void f(ParamType param);

f(expr);    // 调用该模板函数
Case1. ParamType 是一个指针/引用类型(不是通用引用);
  • 如果expr类型是一个引用,则忽略引用部分;
  • 然后对着ParamType,模式匹配expr的类型,以确定T.
template<typename T>
void f(T & param);   // param is a reference

int x = 27;         // int
int const cx = x;   // int const
int const & rx =x;  // refernce to x

f(x);   // T => int, param's type => int&
f(cx);  // T => int const, param's type => const int&
f(rx);  // T => int const, param's type => const int&
  1. 因为cx、ex为const int值,因此,T被推导为const int;
  2. 当传递const对象给引用参数时,需要保持不可更改性,因此T包含了const部分;
  3. 类型推导过程中,会忽略引用类型,所以T不包含&;
template<typename T>
void f(T const &param);   // param is now a ref-to-const

int x = 27;         // int
int const cx = x;   // int const
int const & rx =x;  // refernce to x

f(x);   // T => int, param's type => int const &
f(cx);  // T => int, param's type => int const &
f(rx);  // T => int, param's type => int const &
  1. 因为现在假设ParamType为ref-to-const,所以const不用作为T的一部分推导;
  2. 照常,对于rx,在推导T的过程中,忽略其引用类型;
template<typename T>
void f(T* param);   // param is now a pointer

int x = 27;           // int
int const * px = &x;  // int const *(这是一个指向常量的指针,int * const 则是一个指针常量,是一个常量)

f(&x);   // T => int, param's type => int *
f(&px);  // T => int const, param's type => int const *
  1. 对于指针,推导T的方式,跟引用是一样的;
Case2. ParamType 是一个通用引用;(区别于“左值引用”和“右值引用”)
  • 如果expr是一个左值,T和ParamType都被推导为左值引用;(a.这是仅有的T被推导为引用的情况 b.虽然ParamType的格式是右值引用,但它们的类型仍是左值引用)
  • 如果expr是一个右值,则规则符合Case1;
template<typename T>
void f(T && param);   // param is now a universal reference

int x = 27;         // int
int const cx = x;   // int const
int const & rx =x;  // refernce to x

f(x);   // T => int &, param's type => int &
f(cx);  // T => int const &, param's type => int const &
f(rx);  // T => int const &, param's type => int const &
f(27);  // T => int, param's type => int &&

对于通用引用的类型推导规则,区别于expr是左值还是右值,而不同;
对于非通用引用的类型推导,则完全没有这种特殊的规则;

Case3. ParamType 既不是指针,也不是引用;
  • 如果expr的类型是引用,一样忽略引用部分;
  • 如果expr有const部分,也忽略之;
  • 如果expr有volatile部分,也忽略之;(常用于设备驱动)
template<typename T>
void f(T param);    // param is now passed by value

int x = 27;         // int
int const cx = x;   // int const
int const &rx =x;   // refernce to x

f(x);   // T => int, param's type => int
f(cx);  // T => int, param's type => int
f(rx);  // T => int, param's type => int

因为param是完全独立于x、cx、rx的(即它是一份拷贝),这便是为什么在推导时,忽略expr的const部分的原因;
这是ParamType无任何修饰的情况下才成立的,即当为引用或指针时,const还是无法忽略的;

char* const ptr1 = "abc";        // char * const
const char* const ptr2 = "abc";  // const char * const

f(ptr1); // T => char*, param's type => char *
f(ptr2); // T => char const *, param's type => char const *

这里要注意指针常量,跟常量指针,以及常量指针常量的区别;

以数组为参数

数组类型 于 指针类型,是不用的,虽然有些情况下它们是通用的;
很多情况下,一个数组能衰化成一个指向其第1个元素的指针;

const char name[] = "Hello";    // const char[6]

const char * ptrToName = name;  // const char * 

虽然能将array转成pointer,但这两者的类型,是不一样的;

放到函数参数中:

void myFunc(const char param[]);
void myFunc(const char * param);

myFunc(name);

这两个函数声明是一模一样的,这是C所遗留到C++上的一个内容,因此,对于myFunc(name)的调用,这两个函数都可以,但只能写其中1个,否则会报错;

放到函数模板中:

template<typename T>
void f(T param);

f(name);   // T => char const *, param's type => char const *

对于Case3这种类型的模板函数来说,传入的name数组,因为数组参数声明会被视为指针参数,所以会被推导成 const char *;

解决方法:

template<typename T>
void f(T & param);

f(name);    // T => const char[6], param's type => const char(&)[6]

T会被推导成实际的数组类型,类型包括了数组的size;

以函数为参数

跟数组一样,函数类型也能够衰化成指针类型;

void someFunc(int,double);

template<typename T>
void f1(T param);

template<typename T>
void f2(T& param);

f1(someFunc);   // void(*)(int,double)
f2(somefunc);   // void(&)(int,double)
Things to Rememb
  • 模板类型推导期间,引用的参数,则忽略引用;
  • 当推导“通用引用”参数时,左值参数要特殊对待;
  • 当推导“传值”参数时,const 或 volatile 要忽略之;
  • 模板类型推导期间,数组或函数名称作为参数,会衰化为指针,除非使用引用参数;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值