c++11中的move与forward(二)
; //调用operator(holder&);
h1 = get_holder(); //调用operator(holder&&)
显然后面的实现是更高效的。
写到里,有的人也许提出问题: T&& ref 指向的是右值,那ref本身是左值还是右值?具体来说就是:
1 holder& operator=(holder&& other)
2 {
3 holder h = other;//这里调用的是operator=(holder&) 还是operator=(holder&&)
4 return *this;
5 }
这个问题的本质还是怎么区分rvalue?
c++11中对rvalue作了明确的定义:
Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
如果一个变量有名字,它就是lvalue,否则,它就是rvalue。
根据这样的定义,上面的问题中,other是有名字的变量,因此是个lvalue,因此第3行调用的是operator=(holder&).
好了说这么久,一直没说到move(),现在我们来给出定义:
c++11中的move()是这样一个函数,它接受一个参数,然后返回一个该参数对应的rvalue().
就这么简单!你甚至可以暂时想像它的原型是这样的(当然是错的,正确的原型我们后面再讲)
T&& move(T& val);
那么,这样一个move(),它有什么使用呢?用处大了!
前面用到了std::swap()这个函数,回想一下以前我们是怎么想来实现swap的呢?
1 void swap(T& a, T& b)
2 {
3 T tmp = a;
4 a = b;
5 b = tmp;
6 }
想像一下,如果T是我们之前定义的holder,这里面多做了多少无用功啊,每一个赋值语句,就有一次资源销毁,以及一次拷贝!但如果用上了move().
1 void swap(T& a, T& b)
2 {
3 T tmp=move(a);
4 a = move(b);
5 b = move(tmp);
6 }
这样一来,如果holder提供了operator=(T&&)重载, 上述操作就完全只是交换了3次指针,效率大大提升!
move使得程序员在有需要的情况下,能够把lvalue当成rvalue来使用。
二. forward()
1.转发问题
除了move()语义之外,rvalue的提出还为了解决另一个问题:转发(forward).
假设我们有这样一个模板函数,它的作用是:缓存一些object,必要的时候,创建新的。
复制代码
template
TYPE* acquire_obj(ARG arg)
{
static list caches;
TYPE* ret;
if (!caches.empty())
{
ret = caches.pop_back();
ret->
reset(arg);
return ret;
}
ret = new TYPE(arg);
return ret;
}
复制代码
这个模板函数的作用简单来说,就是转发一下参数arg给TYPE的reset()函数和构造函数,除此它就没有再干别的事情,在这个函数当中,我们用了值传递的方式来传递参数,显然是比较低效的,多了次无必要的拷贝。
于是我们准备改成传递引用的方式,同时考虑到要能接受rvalue作为参数,于是改成这样:
template
TYPE* acquire_obj(const ARG& arg)
{
//...
}
这样写其实很不灵活:
1)首行,如果reset() 或TYPE的构造函数不接受const类型的引用,那上述的函数就不能使用了,必须另外提供非const TYPE&的版本,参数一多的话,很麻烦。
2)其次,如果reset()或TYPE的构造函数能够接受rvalue作为参数的话,这个特性在acquire_obj()里头永远也用不上。
其中1)好理解,2)是什么意思?
2)说的是这样的问题,即使TYPE存在TYPE(TYPE&& other)这样的构造函数,它在acquire_obj()中也永远不会被调用,原因是在acquire_obj中,传递给TYPE构造函数的,永远是lvalue.
哪怕外面调用acquire_obj()时,传递的是rvalue。
holder get_holder();
holder* h = acquire_obj(get_holder());
虽然在上面的代码中,我们传递给acquire_obj的是一个rvalue,但是在acuire_obj内部,我们再使用这个参数时,它却永远是lvalue,因为它有名字。
acquire_obj这个函数它的基本功能只是传发一下参数,理想状况下它不应该改变我们传递参数的类型:假如我们传给它lvalue,它就应该传lvalue给TYPE,假如我们传rvalue给它,它就应该传rvalue给TYPE,但上面的写法却没有做到这点,而在c++11以前也没法做到。
forward()函数的出现,就是为了解决这个问题。
forward()函数的作用:它接受一个参数,然后返回该参数本来所对应的类型。
比如说在上述的例子中(暂时省略参数的原型,后面再介绍):
复制代码
holder* h = acquire_obj(get_holder());
//假设 acquire_obj()接受了一个rvalue作为参数,在它的内部,
TYPE* acquire_obj(arg)
{
//arg本来是rvalue,如果我们直接引用,它会被当成lvalue来使用。
//但如果我们用forward()处理一下,我们却可以得到它的rvalue版本。
//此处 TYPE的构造函数接受的是一个rvalue。
TYPE* ret = new TYPE(forward(arg));
}
//但如果我们传给acquire_obj()的是一个lvalue,
holder h1;
//acquire_obj接受了lvalue作为参数。
acquire_obj(h1);
TYPE* acquire_obj(arg)
{
//此处,TY