《Effective Modern C++》学习笔记之条款一:理解模板型别推导

模板的型别推导是现代C++最广泛应用的特性之一,下面的一小段伪代码可以简单明了的说明函数模板使用场景。函数模板定义如下:

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

而一次模板函数调用如下:

f(expr);

编译期,编译器将会通过expr推导出T及ParamType的型别,这两个往往不一样。因为ParamType常会包含一些修饰词,例如const或者引用之类的。

例如,若模板函数声明如下:

template<class T>
void f(const T& param); 

而调用语句为:

int x = 0; // x类型为int 
f(x);      // T类型为int,ParamType为const int&

此时,ParamType被推导为const int&,T被推导为int,这里需要说明一下,T的型别不仅仅依赖于expr的型别,还依赖于ParamType的形式,具体可以分为以下三种情况进行分析:

情况1:ParamType是一个指针或引用,但不是万能指针或引用

此时,型别推导会进行如下操作:

  1. 若expr是具有引用型别,则先将引用部分忽略
  2. 然后对expr的型别和ParamType进行型别匹配,进而决定T的型别。

结合以上两句并参考以下代码便可以理解:

template<class T>
void f(T& param);  //此时param是引用

int x = 27;         //x是int类型
const int cx = x;   //cx是const int类型
const int& rx = x;  //rx是const int& 类型

//在各个调用时,param及T的推导如下:
f(x);  //T是int,param 为 int&
f(cx); //T是const int,param为 const int&
f(rx); //T是const int,param为 const int&
template<class T>
void f(const T& param);  //此时param是引用

int x = 27;         //x是int类型
const int cx = x;   //cx是const int类型
const int& rx = x;  //rx是const int& 类型

//在各个调用时,param及T的推导如下:
f(x);  // T是int,param为 const int&
f(cx); // T是int,param为 const int&
f(rx); // T是int,param为 const int&
template<class T>
void f(T* param);  //此时param是指针

int x = 27;            
const int* px = &x;   

//在各个调用时,param及T的推导如下:
f(&x);  // T是int,param 为 int*
f(px);  // T是const int,param为 const int*

情况2:ParamType是一个万能引用

万能引用的声明型别写作T&&,其会根据实参类型型别,进行不同的处理:

  1. 如果expr是左值,T和ParamType都会被推导为左值引用,这是T被推导为引用的唯一情况
  2. 如果expr是右值,则按照常规情况进行处理(情况一)

结合以上两句并参考以下代码便可以理解:

template<class T>
void f(T&& param);  //这种格式下,param就是一个万能引用

然后声明了如下变量:
int x = 27;         //x是int类型
const int cx = x;   //cx是const int类型
const int& rx = x;  //rx是const int& 类型

在各个调用时,param及T的推导如下:
f(x);  //x是左值,所以T是int&,param 也为 int&
f(cx); //cx是左值,所以T是const int&,param也为 const int&
f(rx); //x是左值,所以T是const int&,param为 const int&
f(27); //x是右值,所以T是int,param为int&&

关于万能引用,可以参考文章:《Effective Modern C++》学习笔记之条款二十四:区分万能引用和右值引用_木木的博客的博客-CSDN博客

情况3:Paramtype即非指针也非引用,即按值传递

如果是按值传递,则param相当于一个副本。

  1. 若expr具有引用型别,则先忽略其引用部分
  2. 若expr是个const、volatile对象,则也忽略其const、volatile关键字

结合以上两句并参考以下代码便可以理解:

template<class T>
void f(T param);  // param按值传递

然后声明了如下变量:
int x = 27;         //x是int类型
const int cx = x;   //cx是const int类型
const int& rx = x;  //rx是const int& 类型

在各个调用时,param及T的推导如下:
f(x);  //T是int,param 也为 int
f(cx); //T是int,param 也为 int
f(rx); //T是int,param 也为 int

这里需要说明一下,虽然cx或者rx是const类型,而param仍然不具有const属性,这是合理的,因为此时param是rx或cx的一个副本,所以即使对param做修改,并不会影响到rx或cx的值,满足其const属性要求。

另外,若expr是一个指涉到const对象的const指针,且expr按值传递给param:

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

const char* const ptr = "this is test"; 

f(ptr);  //此时T和param均会被推导为const char*

这里星号右侧的const将ptr声明为const,表示其不可以修改其指涉的内存地址,星号左侧的const将ptr指涉的对象修饰为const,即对象内容不可更改。当将ptr传递给f()时,在型别推导中,ptr指涉对象的常量性会被保留,但其自身的常量性会被忽略,即其星号右侧的const属性将被忽略,所以,此时param被推导为const char*。

指针退化:

除了以上三种情况外,还有一种情况,就是指针退化的情况,包括数组退化为数组指针,函数退化为函数指针。

当我们把数组传入到某个数组指针形参时,是可以可以编译通过的,因为数组在传递时,会被退化为数组指针。

但是如果此时我们把ParamType声明为一个引用,再向其传入一个数组,此时数组将不会发生退化,即形参就是一个数组的引用,例如:

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

const char name[13] = “123456789”;
f(name);  // T被推导为const char[10], param将会被推导为const char(&)[10]

有意思的是,我们可以利用声明数组引用这一能力创造出一个模板,用以推导数组中的元素个数:

template <class T,std::size_t N> 
// 以编译期常量形式返回数组尺寸
// 此时入参是const int(&)[9],按照模式匹配规则,此时N为9
constexpr std::size_t arraySize(T(&)[N]) noexcept{ 
    std::cout << N << std::endl;
    return N;
}

int main()
{
    int key[] = { 1,2,3,4,5,6,7,8,9 };
    int new_key[arraySize(key)]; //此时key被推导为 const int(&)[9],返回长度为9
    return 0;
}

同理,函数也是一样,具体逻辑见代码:

int someFunc(int,double);//someFunc是一个函数,类型是:void(int,double)

template <class T>
void f1(T param);   //按值传递


template <class T>
void f2(T& param); //按引用传递


f1(someFunc); //此时param被推导为函数指针:void(*)(int,double)

f2(someFunc); //此时param被推导为函数引用:void(&)(int,double)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chiang木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值