我所知道的Move语义与完美转发
一、临时变量效率问题
///
std::vector<int> f()
{
std::vector<int> v;
// stuff
return v;
}
std::vector<int> arr(f());
//
这部分代码做了三件事情:
1、 创建v
2、 用v构造f()的返回值
3、 用f()的返回值构造arr
问题:在此过程中总共构造了3个v的副本,若能将v直接构造在arr处,就可以避免2次
不必要的拷贝构造过程。怎么办呢?
在C++11之前,这种构造开销通过RVO(返回值优化)来解决,但是对于如下代码:
//
std::vector<int> arr;
arr = fun();
//
编译器就无能为力了。
直到C++11的到来,该问题才被完美解决。
二、右值引用
为了解决临时变量的效率问题,C++11引入了“右值引用”。在接下来的文章中,我
将逐步解释“右值引用”如何成为美貌与智慧并重、英雄与狭义的化身。
左值、右值是表达式的属性,而非对象的属性。左值在表达式结束之后任然存在,而
右值在表达式结束之后就会析构。
根据右值定义,第一节提到的f()就属于右值。
为了说明后续的问题,我将C++11标准的若干规则罗列如下:
假设有类型T,T&和const T&表达了传统的左值引用,而T&&表达新引入的右值引用。
绑定规则:
1、const T &和T&&可以绑定到右值;
2、T&只能绑定左值;
3、右值优先绑定到T&&;
引用折叠规则:
T & & ==> T &
T & && ==> T &
T && & ==> T &
T && && ==> T &&
是的,我目前只能告诉你这些规则,至于为什么C++标准这样定义规则是合理的,我
还没法做出有说服力的回答。
三、Move 语义
还记得std::vector的swap方法么?
//
std::vector<int> tmp;
// tmp stuff
std::vector<int> arr;
arr.swap(tmp);
//
要是能确定tmp不再有用,那么将tmp的内容swap过来是非常优雅和高效的手段。实际上,
Move semantic 正是这种思想。
问题是怎么表明tmp不再有用?C++11标准库提供了一个方法模板:
template<typename T>
std::remove_reference<T>::type && move(T &&);
对于move的实现放在后边的章节解释,目前只需要明白std::move<T>(x)调用返回对x的右值
引用。
假设有如下实现的类:
//
class M
{
public:
M(std::vector<int> && v) // move constructor
{
m_v = std::move(v);
}
M(const std::vector<int> & v)
{
m_v = v;
}
M& operator=(std::vector<int> && v) // move assignment
{
m_v.swap(v);
}
M& operator=(const std::vector<int> & v)
{
m_v.reserve(v.size());
std::copy(m_v.begin(), v.begin(), v.end());
}
std::vector<int> m_v;
//...
}
//
模板的第一个构造函数称为:move构造函数,形参为右值引用。相对于第二个传统的
拷贝构造函数,它的实现高效、优雅了太多。
考虑下面的调用
//
M m(f());
std::vector<int> tmp;
m = tmp;
m = std::move(tmp);
//
根据绑定规则,f()优先绑定到右值引用,因此m将通过move构造函数构造。然后,调用
普通赋值操作为m赋值,之后又调用move赋值操作为m赋值。
四、完美转发
template<typename T>
class P
{
}
我所知道的Move语义与完美转发
最新推荐文章于 2024-02-05 00:49:45 发布