wzp's garden / 学习

我对C++11右值引用的理解

2017-09-01 posted in [学习]

lvalue reference和rvalue reference

首先,什么是lvalue和rvalue?

在C中,lvalue是=左边,rvalue是=右边。在C++中,略有不同,不过可以这样简单理解:如果一个object是一个lvalue,我们用它本身(内存地址), 如果一个object是一个rvalue,我们用它的值(内存中的内容)。

那么,什么是lvalue reference和rvalue reference?

C++中正常的引用,就是lvalue reference,它是对另一个lvalue的引用,如int b = 1; int& a = b;。而rvalue reference是对一个rvalue的引用,例如int&& a = 1;。 C++之所以出现rvalue reference,主要是为了效率。因为rvalue是一个临时的东西,所以可以保证rvalue reference指向的东西只有它自己可以用,别人动不到这块内存, 因此这这块内存可以安全地任意折腾。

需要注意的是,在int&& a = 1;中,a是对1的rvalue reference,但是a本身是一个lvalue,毕竟a有name,有地址。

std::forward

std::forward用于完美转发(其实就是个强制类型转化啦)。考虑如下函数:

template<typename T>
void Foo(T&& t) {
  SomeFunc(std::forward<T>(t));
}

std::forward的作用则是t传给Foo是什么类型,就将什么类型原封不动地传给SomeFunc。

它是怎么实现的呢?直接给出std的source code:

// Overload 1
template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type& __t) noexcept
    { return static_cast<_Tp&&>(__t); }

// Overload 2
template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    {
      static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
		    " substituting _Tp is an lvalue reference type");
      return static_cast<_Tp&&>(__t);
    }    

当调用Foo(1);时,实参是一个rvalue。由于T的类型是int,t是一个lvalue,则调用std::forward(int& t)版本(overload 1),该重载返回int&&,因此传给SomeFunc的 实参类型保持一致。

当调用int a = 1; Foo(a);时,实参是一个lvalue。由于T的类型是int&,t是一个lvalue,则调用std::forward<int&>(int& t)版本(overload 1),该重载返回int&&& -> int&,因此传给SomeFunc的 实参类型保持一致。

什么时候会调用Overload 2呢?嘿嘿,试试std::forward(std::forward(t))并想想为什么吧。

std::move

std::move用于将任意类型转换为rvalue reference类型(也是个强制类型转换啦)。直接看实现:

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

可以看到,无论传给std::move是lvalue还是rvalue,最终都是得到rvalue reference.

补充一句,为什么要用std::move呢?因为一般来说rvalue reference用于move constructor以提高构造效率(不用copy,直接把内存拿过来用)。如果一个object你不想要了,想扔给move constructor构造一个新的object,就可以用std::move(object)传给构造函数,来调用move contructor. 如果不用std::move,那就变成普通的copy construct了。

Ubuntu14.04LTS安装后踩坑

2016-09-20 posted in [学习]

C++11 Memory Model

2016-07-05 posted in [学习]

Notes for Reinforcement learning

2016-06-30 posted in [学习]

跟我一起写web server

2015-10-09 posted in [学习]

python virtualenv使用

2015-10-09 posted in [学习]

Python爬虫

2015-10-09 posted in [学习]

Momentum方法

2015-09-28 posted in [学习]

Gflags文档(my翻译)

2015-09-27 posted in [学习]

Learn how to learn笔记

2015-08-14 posted in [学习]

网络流之Push Relabel算法

2014-12-22 posted in [学习]