c++ std::move和std::forward总结与使用

右值和左值的区别:

当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
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

你可能感兴趣的:(c++,c++,开发语言)