右值和左值的区别:
当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
C++/C++11中左值、左值引用、右值、右值引用的使用
std::move解析
std::move的唯一功能就是将一个左值引用强制转化为右值引用
std::string str = "hello";
std::vector<std::string> v;
//调用拷贝构造函数
v.push_back(str);
//调用移动构造函数,掏空str,掏空后,不可以再使用str
v.push_back(std::move(str));
std::move源码解析
std::move的函数原型定义:
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type &&>(t);
}
首先函数参数T&&是一个指向T类型的右值引用,通过引用折叠,将T&&传递过来的类型,右值保持不变,左值变成普通的左值引用,然后在通过remove_reference::type模板移除T&&,T&的引用,获取到具体的类型T
//原始的,最通用的版本
template <typename T> struct remove_reference{
typedef T type; //定义T的类型别名为type
};
//部分版本特例化,将用于左值引用和右值引用
template <class T> struct remove_reference<T&> //左值引用
{ typedef T type; }
template <class T> struct remove_reference<T&&> //右值引用
{ typedef T type; }
//举例如下,下列定义的a、b、c三个变量都是int类型
int i;
remove_refrence<decltype(42)>::type a; //使用原版本,
remove_refrence<decltype(i)>::type b; //左值引用特例版本
remove_refrence<decltype(std::move(i))>::type b; //右值引用特例版本
引用折叠,其实就是多个引用的意思,所有的引用折叠最终都代表一个引用,要么是左值引用,要么是右值引用
规则就是:
如果任一引用为左值引用,则结果为左值引用,否则为右值引用
比如 int& &&等价于int &
这个的应用场景主要为函数模板的推导
https://zhuanlan.zhihu.com/p/50816420
std::forward解析
在任何的函数内部,对形参的直接使用,都是按照左值进行的,比如:
template<typename T>
void func(T& t){
std::cout<<"func(T& t)"<<std::endl;
}
template<typename T>
void func(T&& t){
std::cout<<"func(T&& t)"<<std::endl;
}
template<typename T>
void printType(T&& t){
func(t);
}
int main()
{
int i = 0;
printType(i);
printType(2);
}
这段代码的打印为:
func(T& t)
func(T& t)
那我们要怎么让调用的函数按我们的预期执行呢,答案就是使用std::forward:
template<typename T>
void printType(T&& t){
func(std::forward<T>(t));
}
打印为:
func(T& t)
func(T&& t)
std::forward源码解析
template <class _Tp>
_Tp&&
forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
{
return static_cast<_Tp&&>(__t);
}
template <class _Tp>
_Tp&&
forward(typename remove_reference<_Tp>::type&& __t) _NOEXCEPT
{
return static_cast<_Tp&&>(__t);
}
首先通过remove_reference拿到_Tp的直接类型,如int类型
- 对于第一个函数,通过remove_reference,t的类型是int&,再通过static_cast中的引用折叠,static_cast<_Tp& &&>,最终拿到的是左值引用
- 对于第二个函数,通过remove_reference,t的类型是int&&,再通过static_cast中的引用折叠,static_cast<_Tp&& &&>,最终拿到的是右值引用
因此这样实现了完美转发
参考文档:
https://blog.csdn.net/daaikuaichuan/article/details/88371948