右值引用和std::move 以及std::forward(完美转发)

本文详细解析了C++中左值与右值的概念,包括它们的区别与应用场景,探讨了左值引用与右值引用的工作原理,以及std::forward与std::move如何实现完美转发与移动语义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

左值与右值

左值是这样一种表达式,它指向一块物理内存,并允许我们通过运算符&来取得这块内存的地址,而右值则是非左值的表达式。常量和匿名的临时变量都为右值,如函数返回值。

比如:int a=0; //a为左值

          string s="ss";//s为左值,"ss"为右值

          string ss=string("e");//ss为左值,string("e")为右值

左值引用与右值引用

右值引用左值引用都是引用,都是一个变量(即都是一个左值),左值引用通过在类型名后加 & 来表示,而右值引用则通过在类型名后加 && 表示。只不过左值引用引用的是左值,而右值引用只能引用右值。函数形参都是左值,因为函数形参都有名称,都可以对形参进行取地址操作。

右值与右值引用

右值不是左值,右值引用是左值,是对右值的一种引用。c++11引入右值引用主要是为了实现移动语义和完美转发。右值引用本身没什么意义。

std::forward与std::move

std::forward实现

template<class TPYE>
TPYE&& forward(typename remove_reference<TPYE>::type& a) noexcept
{
  return static_cast<TPYE&&>(a);
}

先看remove_reference的实现,顾明思议就是去掉引用:

template< class T > struct remove_reference      {typedef T type;};
template< class T > struct remove_reference<T&>  {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};

std::forward实现了完美转发看下面的代码(参考链接:https://blog.youkuaiyun.com/victorydh123/article/details/73603830):

void f(int&& a) {
    std::cout << "rvalue" << std::endl;
}

void f(int& a) {
    std::cout << "lvalue" << std::endl;
}

template<typename T>
void test(T&& a) {
    f(forward<T>(a));//如果没有forward 将永远调用左值版本,因为右值引用是左值。右值引用的形参只能接受右值。完美转发实现了传递进来什么类型,我就返回什么类型。其实就是解决了右值引用形参传入后,形参本身已经是一个左值了,不能作为下一个函数的右值引用参数。
}

template<typename T>
T&& forward(std::remove_reference_t<T>& arg)
{
    return static_cast<T&&>(arg);
}

int main()
{

    int a = 10;
    test(a);//"lvalue"
    test(std::move(a));//"rvalue"

    //system("pause"); //for windows VS to pause to see the output
    return 0;
}

模板推断时(请百度通用引用),引用的折叠规则:

& & -> & 
& && -> & 
&& & -> & 
&& && -> && 

当参数是一个左值时,在通用引用模板类型推断的时候,左值会被推断成左值引用,不管传递的参数是左值类型还是左值引用,通通变成左值引用。

std::move的实现代码也很简单

template<typename _Tp>  
inline typename std::remove_reference<_Tp>::type&&  move(_Tp&& __t)  
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); } 

就是将__t转为右值引用返回。

 

看上去move什么也没做,没错std::move并不产生新的对象,不会调用构造函数,只是做了一个类型转换,这样做的目的主要是为了支持移动构造和移动赋值,因为移动构造和移动赋值的参数类型为右值引用。

要了解移动构造和移动赋值请百度“移动构造和移动赋值”或者参考这篇博客:https://www.cnblogs.com/ldlchina/p/6608154.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值