C++11之move与forward

右边值引用

右值引用是C++11中引入的新特性 , 它实现了转移语义和精确传递。它的主要目的有两个方面:

  1. 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。

  2. 能够更简洁明确地定义泛型函数。

1. 左值与右值的区别

右值引用和左值引用的区别:

  1. 左值可以寻址,而右值不可以。
  2. 左值可以被赋值,右值不可以被赋值,可以用来给左值赋值。
  3. 左值可变,右值不可变(仅对基础类型适用,用户自定义类型右值引用可以通过成员函数改变)。
int a = 0;   // a是左值,0是右值
int b = rand();  // b是左值,rand()是右值
//左值有名称,可根据左值获取其内存地址,而右值没有名称,不能根据右值获取地址。

右值引用作用:大部分时间和左指引用一样,但是有一点区别,他能够绑定到临时变量(右值)
延长了右值的生命周期。
类的右值是一个临时对象,如果没有被绑定到引用,在表达式结束时就会被废弃。于是我们可以在右值被废弃之前,移走它的资源进行废物利用,从而避免无意义的复制。
(1)避免拷贝,提高性能,实现move(),避免无意义复制 、析构开销减少
(2)避免重载参数的复杂性,实现forward()

2. 引用叠加规则、右值引用的特殊类型推断规则

左值引用A&和右值引用A&& 可相互叠加
即:X& &、X& &&、X&& &都折叠成X&
X&& &&折叠为X&&

A& + A& = A&
A& + A&& = A&
A&& + A& = A&
A&& + A&& = A&&

举例示例,void foo(T&& x)中,如果T是int&, x为左值语义,如果T是int&&, x为右值语义。

右值引用的特殊类型推断规则:当将一个左值传递给一个参数是右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参的左值引用,如:

template 
void f(T&&);

int i = 42;
f(i)
//上述的模板参数类型T将推断为int&类型,而非int。
3. std::move的作用

减少一次资源的创建与释放。
而是仅仅复制了指针,并将源对象的指针置空。因为源对象是一个右值,不会再被使用。这样的构造函数被称为“转移(移动)构造函数”。
一个左值转换为右值。如此可以让对象可以在转移语义中使用。

//如果类X包含一个指向某资源的指针,在左值语义下,类X的赋值构造函数如下:
X::X(const X& other)
{
  // ....
  // 销毁资源 
  // 复制other的资源,并使指针指向它
  // ...
}

//应用代码如下,其中,tmp被赋给a之后,便不再使用。
X tmp;
// ...经过一系列初始化...
X a = tmp;
//首先执行一次默认构造函数(tmp申请资源),再执行一次复制构造函数(a复制资源),
//最后退出作用域时再执行一次析构函数(tmp释放资源)。
//--------------------------------------------------------------------------------------
如下实现move:
X::X(const X& other)
{
  // 交换this和other的资源        
}
4. std::move的实现

std::move用于强制将左值转化为右值。其实现方式如下:

template 
typename remove_reference::type&&
std::move(T&& a) noexcept
{
  typedef typename remove_reference::type&& RvalRef;
  return static_cast(a);
}

当a为int左值(右值)时,根据引用叠加原理,T为int&, remove_reference = int, std::move返回类型为int&&,即右值引用。

std::move(string("hello"))调用解析:

  • 首先,根据模板推断规则,确地T的类型为string;
  • typename remove_reference::type && 的结果为 string &&;
  • move函数的参数类型为string&&;
  • static_cast(t),t已经是string&&,于是类型转换什么都不做,返回string &&;

string s1("hello"); std::move(s1); 调用解析:

  • 首先,根据模板推断规则,确定T的类型为string&;
  • typename remove_reference::type && 的结果为 string&
  • move函数的参数类型为string& &&,引用折叠之后为string&;
  • static_cast(t),t是string&,经过static_cast之后转换为string&&, 返回string &&;
5. 完美转发

完美转发实现了参数在传递过程中保持其值属性的功能,即若是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值。

6. std::forward()

std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。

class Foo
{
public:
    std::string member;

    template
    Foo(T&& member): member{std::forward(member)} {}
};
6. 对比
  • std::move执行到右值的无条件转换。就其本身而言,它没有move任何东西。
  • std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。
  • std::move和std::forward只不过就是执行类型转换的两个函数;std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有做任何事情。它们没有产生需要执行的代码,一byte都没有。
  • std::forward()不仅可以保持左值或者右值不变,同时还可以保持const、Lreference、Rreference、validate等属性不变。

你可能感兴趣的:(C++11之move与forward)