C++ 右值引用的初步理解

在C++11中,添加了右值引用是对于经常使用Stl的人来说非常有意义的事情。

初步理解为 Move语义对于性能的提升。

std::string("abc") //“abc为临时变量,这里将调用move语义 将abc移动到容器中。

std::vector<std::vector<int>> v;

std::vector<int> vv;

vv.pushback(1);

...//dosomething

v.pushback(std::move(w));  //几乎没有开销。

printf("%d\n",w.size());  //   print 0

以下引用自其它博文的说法

http://blog.chinaunix.net/uid-13614124-id-5750377.html

c++中临时对象可能会大量存在,比如string/STL调用中。临时对象中的资源(buffer或者字符串)会随着临时对象的析构而消失,所以一般会在对象析构前把它们(资源)拷贝出来,而不会直接引用这些资源,否则会野指针。

其实,既然已经是临时对象了,马上就会被析构掉,所以赶在析构前改一下里面的内容也不会带来太多负面影响,当然是在控制良好的前提下。基于这个思路,我们可以放心大胆的直接引用临时对象中的资源指针(比如指针赋值,引用该资源),然后把它置空(防止被临时对象释放掉,囧),或者更屌一点的,把我们意图释放的指针,直接赋值给临时对象的该指针,借助临时对象在析构时会释放它的资源的时机,把我们想释放的东西给释放掉!

多么精妙!

在c11之前,临时对象只能以 const MyClass& my_object 这种方式传递,有const在,所以临时对象是不能被修改的。但是c11引入了右值引用 MyClass&&,导致我们可以修改临时变量了!

假如你是函数的提供者,你提供了带右值引用的参数,那么,你就大胆的去引用它的资源,修改它的资源吧,不用担心它还会被使用到,当函数返回之后。因为它是右值引用,该函数的调用者会保证这个参数不会再被使用到。

假如你是函数的调用者,当你发现你有一个左值对象my_object即将不再被使用到了,就可以用move语义把它转成右值引用丢给函数,任函数去修改它的内容无所谓,因为你确定它不再被使用了(如果你确定不了,那就不能使用move语义)。当然,假如你处理的已经是右值了(比如函数返回值),那就跟以前没什么两样。



_______________________________________

2018.02.06

今天在工作中继续学习模板编程相关问题时,又遇到了右值引用和完美转发相关问题。

一年后我又回来看我当初的理解,还是那句话 too young too simple sometimes native!

回头再看当时的理解基本已经觉得是错误的了。现在就回过头来再议这个C++ 右值引用相关问题。


在我们上面的肤浅理解中,把 std::move 作为了很多东西的第一推动力,这是相当错误的。

经过一年的沉淀,其实对于 std::move来说,只做了一件事。可以初步的理解为 (不过当然是错误的)

template<typename T>
T&& move(T& val)
{
    return static_cast<T&&>(val);
}
move 只是纯粹的将一个左值转化为了一个右值,但当年误打误撞的代码有效果的原因在于,MSVC 14.0的 STL实现基本都已经实现了移动语义,相当于对于 vector<T>::push_back()有两个版本的实现,简单写如下:

template<typename T>
class Vector
{
    void push_back(T& lval);
    void push_back(T&& rval);
};
而对应的类型 T 也实现了移动拷贝,如下:

class T
{
    T(T& other)
    {
        // copy constructor
    }

    T(T&& other)
    {
        // move constructor
    }
};
所以,整个代码跑起来的确有性能提升,真是瞎猫碰见死耗子哈哈哈。


再来说一下原来没有碰触的完美转发 std::forward()

	// TEMPLATE FUNCTION forward
template<class _Ty> inline
	constexpr _Ty&& forward(
		typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
	{	// forward an lvalue as either an lvalue or an rvalue
	return (static_cast<_Ty&&>(_Arg));
	}

template<class _Ty> inline
	constexpr _Ty&& forward(
		typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
	{	// forward an rvalue as an rvalue
	static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
	return (static_cast<_Ty&&>(_Arg));
	}
当我们将一个右值引用传入函数时,他在实参中有了命名,所以继续往下传或者调用其他函数时,根据C++ 标准的定义,这个参数变成了一个左值。那么他永远不会调用接下来函数的右值版本,这可能在一些情况下造成拷贝。为了解决这个问题 C++ 11引入了完美转发,根据右值判断的推倒,调用forward 传出的值,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。

这样的处理就完美的转发了原有参数的左右值属性,不会造成一些不必要的拷贝。代码如下:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    string A("abc");
    string&& Rval = std::move(A);
    string B(Rval);    // this is a copy , not move.
    cout << A << endl;   // output "abc"
    string C(std::forward<string>(Rval));  // move.
    cout << A << endl;       /* output "" */
    return 0;
}

这才是整个右值引用正确的理解。

当然我希望我一年之后再回来看这篇博客,又能写一些新的东西。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值