c++11中的std::move()和std::forword()

在了解二者之前需要先了解一下左值右值的概念。这是学习二者的基础。

1、左值与右值的概念

引用c语言中的概念:左值既可以出现在等号左边又可以出现在等号右边,而右值则只能出现在等号右边,这就是二者的区别。
左值的特点:可以寻址的变量,可持久性。
右值的特点:不能寻址的常量,或者是表达式求值过程中创建的无名临时对象、或者短暂性的。

2、左值引用与右值引用

左值引用:引用的是一个对象
右值引用:必须绑定到右值的引用,可实现“移动语义”,通过"&&"获取右值引用。

int  x = 5;  //x为左值
int &y = x;  //y为左值引用
int &&z = x * 6; //z为右值引用,  因为当x*6 在等号左边的时候表达式不成立
int & z1 = x * 6;  //错误,因为x * 6 是一个右值
const int &z2 = x * 6;  //正确,可以将一个const引用绑定到一个右值上。

3、二者区别

std::move()参数传入右值时候是无条件转换,而std::forword()参数传入右值的时候是有条件转换。如果传入的值都是右值二者是没有区别的。

4、引用折叠规则

如果间接的创建一个引用的引用,这个时候引用就会被折叠一个普通的左值引用类型,有一种情况除外,右值引用的右值引用会被折叠成一个右值引用,即T && && 会被折叠成T &&

  • T& &,T&& & ,T& && 会被折叠成 T&
  • T&& && 会被折叠成T&&

5、右值引用推断规则

当一个左值传递给一个参数为右值引用的函数,并且该函数是一个函数函数模板,根据传入的值来推断模板参数类型。

template <typename T>
void func(T &&);
int i = 10;
f(i);
//推断结果 T类型为int& 而不是 int
因为接受的是i,而i是可以正常传入func中的,所以T && 必须是一个左值的引用,
根据折叠规则,T 只有为int &的时候才能折叠成 T&的样子,所以T 推断类型为int &.

6、std::move()

为什么这个函数可以将一个左值转换成一个右值呢?先看一下标准库中函数定义:

template<typename T>
typename remove_reference<T>::type && move(T&& t)
{
    return static_cast<typename remove_reference<T>::type &&>(t);
}

很明显这需要用到右值引用的推断规则。

  • std::move(string(“world”)); //可以看出string("world")是一个右值,因为是一个匿名对象。
    1、 根据右值引用推断规则可以知道,要想传递一个右值接收的参数必须是一个右值引用,所以T 的类型为 string
    2、 move()的参数类型为string&&
    3、 static_cast<typename remove_reference<string>::type &&>(t)转变为static_cast<string &&>(t) 因为t的类型为string && 而强转的类型也是,所以什么操作都不做,返回string&&
  • string s("world"); std::move(s) 可以看出传入的值s是一个左值。
    1、模板参数要能接收左值必须要用左值的引用才能接收,根据折叠规则可以推到出T 类型为string &这样的话,才能使T&&转变为string &
    2、move()参数参数类型为string &.
    3、 static_cast<typename remove_reference<string>::type &&>(t)转变为static_cast<string &&>(t) 因为t的类型为string &会被强制转化为string &&类型。

由此可以看出std::move()能够接收左值和右值背后都是static_cast()在搞鬼。
这就是std::move()能无条件转换的原因。

void func(int&& a)
{
    cout << a << endl;
}
int a = 6;
func(std::move(a));
int b = 10;
func(static_cast<int&&>(b)); 

//以上二者的看似不一样实则一样。

7、std::forword()

源码

1template<class T> inline
constexpr T&& forward(typename remove_reference<T>::type& _Arg)
{ 
 return (static_cast<T&&>(_Arg));
}
2template<class T> inline
constexpr T&& forward(typename remove_reference<T>::type&& _Arg)
{ 
 return (static_cast<T&&>(_Arg));
}

举例分析

class Foo
{
public:
    std::string member;
    template<typename T>
    Foo(T&& member): member{std::forward<T>(member)} {}
};

传入一个左值
1、当传入的参数是一个左值,根据以上推到,可知T = string &
2、std::forward<T>(member) 转化为std::forward<string &>(member)
3、传入的值是一个左值,返回的是一个左值引用。
传入一个右值
1、根据以上推到,可知T = string.
2、std::forward<T>(member) 转化为std::forward<string>(member)
3、传入一个右值,返回的也是一个右值。

8、二者对比

  • std:move() 传入的参数无论是左值还有右值最终都会被转换为右值。内部主要是static_cast<>() 做了强转操作。
  • std::forword()当传入左值的时候返回的是左值,传入右值的时候返回的是右值,不会改变值的属性。

9、例子

#include <iostream>
#include <memory>
 
using namespace std;
 
 
struct tar_str
{
    tar_str(int && n)
    {
        cout <<"rval n = "<< n <<endl;
    }
    tar_str(int & n)
    {
        cout <<"lval  n = "<< n << endl;
    }
 
};
 
class testB
{
public:
 
    template<class T1,class T2,class T3>
    testB(T1 && t1,T2&& t2,T3 && t3): 
        m_a1(std::forward<T1>(t1)),
        m_a2(std::forward<T2>(t2)),
        m_a3(std::forward<T3>(t3))
    {}
private:
    tar_str m_a1, m_a2, m_a3;
};
 
template<class T,class U>
std::unique_ptr<T> make_unique1(U && u)    //(U && u) 表示支持左值和右值传参
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)));
    //return std::unique_ptr<T>(new T(std::move(u)));   //把所有的都改成右值
}
 
 
template<class T, class ...U>
std::unique_ptr<T> make_unique2(U &&... u)   //表示可以传多个参数
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)...));
    //return std::unique_ptr<T>(new T(std::move(u) ...)); //创建对象的时候传递多个值
}
 
int main()
{
    auto p1 = make_unique1<tar_str>(2);
 
    int i = 10;
    auto p2 = make_unique1<tar_str>(i);
 
    cout <<"---------------------------"<<endl;
    int j = 100;
    auto p3 = make_unique2<testB>(2, i, j);
    return 0;
}

使用return std::unique_ptr<T>(new T(std::forward<U>(u))) 运行结果
在这里插入图片描述
使用return std::unique_ptr<T>(new T(std::move(u)))运行结果

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值