🗒️std::forward 原理解析
type
status
date
slug
summary
tags
category
icon
password
最近在阅读C++ Primter中关于模板的部分,发现网上一些博客对Forward的原理有不同的解读,翻阅了eel.is的相关draft,这里给出自己的理解。
从类型推导开始
推导 & 引用
在C++17之前,模板只支持函数的类型推导。而在推导规则当中,有很重要的一点:
对于模板参数
typename T
而言,只有参数类型为 T&&
,即对类型的右值引用,会导致类型推导为引用类型。参照eel.is:
许多博客将对左值引用的推断解释为引用,是错误的。左值引用的推断只会推断出基本类型。即:
其中,
f(a)
生成的模板实例当中,T为 int
,而非引用类型。而当参数为右值引用时,推导结果如下:
- 传入左值,
T
推导为左值引用
- 传入右值,
T
推导为基本类型
只有参数为右值引用的时候,T才有可能被推断为左值引用!
引用折叠
通常来讲,我们无法使用引用来初始化引用。一个引用类型仅在
decltype
不会被视为引用的对象(别名)。而现在,我们有了两种方式来使引用指向引用:
- 别名,
typedef
orusing
- 对右值引用参数的模板参数的推断
折叠的规则如下:
- 当且仅当 &&+&& 时,折叠为右值引用 &&。
- 其他情况如 &+&,&&+&时,均折叠为左值引用。
完美转发?Why?
右值引用 & 解析
要理解为什么需要完美转发,需要理解右值引用的解析规则,或者说生命周期。
参考eel.is中,关于表达式解析的章节:
对于有名的(named)右值引用,将被当作左值引用对待。
这点非常重要,继续回忆函数传参的过程。C++ Primer中提到
Arguments are used to initialize parameters.
而我们的参数基本上都是有名(named)的,这就导致我们无法将右值引用显性的传递给子函数。如果我们直接传递右值引用变量,会被解析为左值引用传递。而左右值的语义是我们分辨move和copy的关键。
因此,我们需要一个方法,帮助我们在传递参数的过程中,保持参数的左右值语义。
来了,std::forward
!
先欣赏源码:
其中,
remove_reference
利用模板匹配的规则,帮助我们获得原始类型,具体细节这里不细讲。假设我们有如下函数需要进行转发,也就是我们的场景:
为了便于区分,我们的模板使用
X
作为类型,而X
的基本类型,我们简写为BaseX
。回忆上文讲到的推测规则!
当传入右值时,X被推测为基础类型
此时,
X = BaseX
,会匹配到则会保持右值引用的属性,因为返回类型为
T&&
,而我们传入的是X
,此时X
本身不带引用,则会返回一个右值引用(unnamed,无名),而无名的右值(或者说xvalue形式)引用会保持其右值的语义,完美转发了右值引用。当传入左值时,X被推测为引用类型
此时,
X = BaseX&
,会匹配到此时,
T = BaseX&
,T&&
则会触发引用折叠,即 T&& = BaseX &&&
,因此返回一个左值引用类型。总结
本文总结了std::forward的相关原理,帮助大家理解右值引用对类型推断的作用,希望对大家有所帮助。相同的有类似std::move的源码,大家可以去看下是否能够理解。
上一篇
本站的搭建过程
下一篇
UDP数据包大小的讨论
- Twikoo