一、右值引用主要用来解决C++ 98/03中遇到的两个问题:
1、 临时对象非必要的昂贵的拷贝操作
1)临时对象来构造一个新的对象,调用拷贝构造函数,需要创建一个和临时对象一样的堆,并释放临时对象所指向的堆。
2)需要引入移动语义——移动构造函数和移动赋值构造函数,它可以让新对象的堆直接指向临时对象的堆,从而省去一次创建堆和释放堆的操作。
3) 为了使左值能够使用移动语义,需要引入move——强制将左值转化为右值,从而能够使用移动语义(只有右值才能使用移动构造函数)。
2、 在模板函数中如何按照参数的实际类型进行转发
当在函数模板中,调用另一个函数并将函数模板接收到的参数传给它时,如果函数模板接收的参数是右值,此时在另一个函数中接收到的参数就变成了左值,为了保证另一个函数接收到的依然是右值,此时引入完美转发——std::forward。
二、右值引用相关的概念:
右值、纯右值、将亡值、universal references(未定的引用类型)、引用折叠、移动语义、move语义和完美转发等等
右值包含纯右值(字面值常量)和将亡值(临时对象),
区分左值、右值的方法:
看能不能对表达式取地址,能取地址就是左值,不能取地址就是右值。
所有的具名变量都是左值,而匿名变量则是右值。
C++11中,所有的值必须是左值、将亡值、纯右值三者之一。
纯右值:
非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和lambda表达式
将亡值:
C++11新增的、与右值引用相关的表达式:将要被移动的对象、T&&函数返回值和转换为T&&类型的转换函数的返回值
引用折叠的规则:
1)所有的右值引用叠加到右值引用仍然还是一个右值引用
2)所有的其他引用类型之间的叠加都将变成左值引用
A& & 折叠成 A&
A& && 折叠成 A&
A&& & 折叠成 A&
A&& && 折叠成 A&&
右值引用的第一个特点:
通过右值引用的声明,右值又“重获新生”,其生命周期和右值引用类型变量的生命周期一样长,只要该变量还活着,该右值临时量将会一直存活下去。
右值引用的第二个特点:
右值引用独立于左值和右值,即右值引用类型的变量可能是左值,也可能是右值。
右值引用的第三个特点:
T&& t在发生自动类型推断的时候,它是未定的引用类型(universal references),如果被一个左值初始化,它就是一个左值,如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。
代码示例如下:
template <typename T>
void fun(T&& t)
{
}
int main() {
fun(10); //编译器自动推导成 void fun<int>(int&& t),此时T就推导成int类型
int val = 11;
fun(val); //编译器自动推导成 void fun<int&>(int& t),此时T就推导成int&类型
}
初始化参数为右值时,自动推导为右值

初始化参数为左值时,自动推导为左值

正是因为右值引用可能是左值也可能是右值,依赖于初始化,并不是一下子就确定的特点,我们可以利用这一点做很多文章,比如后面介绍的移动语义和完美转发。
三、完美转发
右值引用T&&是一个universal references,可以接受左值或者右值,正是这个特性让他适合作为一个参数的路由,然后再通过std::forward按照参数的实际类型去匹配对应的重载函数,最终实现完美转发。
完美转发的一个示例:
template <typename T>
void processVal(T &val)
{
cout << "左值" << endl;
}
template <typename T>
void processVal(T&& val)
{
cout << "右值" << endl;
}
//不使用完美转发
template <typename T>
void func(T&& val)
{
processVal(val);
}
//使用完美转发
template <typename T>
void funcForward(T&& val)
{
processVal(std::forward<T>(val));
}
int main()
{
//不使用完美转发
func(10); //参数变为右值
int val = 11;
func(val); //参数保持左值
//使用完美转发
funcForward(10); //参数保持右值
funcForward(val);//参数保持左值
getchar();
return 0;
}
运行结果

四、move、forward和swap
1、move的代码实现:
template <typename T>
typename remove_reference<T>::type&& move(T&& param)
{
using ReturnType = typename remove_reference<T>::type&&;
return static_cast<ReturnType>(param);
}
remove_reference<T>::type是T取出引用之后的类型,所以remove_reference<T>::type&&一定是右值引用,
remove_reference<T>::type&一定是左值引用。
T&& param存在于模板推导中,是一个未定义的引用类型,如果实参是左值,则推导为左值引用,如果实参是右值,则推导为右值引用
2、swap代码实现:
c++11之前swap的实现:
template <typename T>
void swap(T& a, T& b)
{
T tmp{a}; // 调用复制构造函数
a = b; // 复制赋值运算符
b = tmp; // 复制赋值运算符
}
c++11中swap的实现:
template <typename T>
void swap(T& a, T& b)
{
T temp{std::move(a)}; // 调用移动构造函数
a = std::move(b); // 调用移动赋值运算符
b = std::move(tmp); // 调用移动赋值运算符
}
3、forward的代码实现:
template<typename T>
T&& forward(typename remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}
下面是remove_reference的实现:
template <class T>
struct remove_reference {
using type = T;
};
// 特化版本
template <class T>
struct remove_reference<T&> {
using type = T;
};
template <class T>
struct remove_reference<T&&> {
using type = T;
};
参考:https://zhuanlan.zhihu.com/p/54893850
https://zhuanlan.zhihu.com/p/54050093
再看一个动态数组的示例:
template <typename T>
class DynamicArray
{
public:
explicit DynamicArray(int size) :
m_size{ size }, m_array{ new T[size] }
{
cout << "Constructor: dynamic array is created!\n";
}
virtual ~DynamicArray()
{
delete[] m_array;
cout << "Destructor: dynamic array is destroyed!\n";
}
// 复制构造函数
DynamicArray(const DynamicArray& rhs) :
m_size{ rhs.m_size }
{
m_array = new T[m_size];
for (int i = 0; i < m_size; ++i)
m_array[i] = rhs.m_array[i];
cout << "Copy constructor: dynamic array is created!\n";
}
// 复制赋值操作符
DynamicArray& operator=(const DynamicArray& rhs)
{
cout << "Copy assignment operator is called\n";
if (this == &rhs)
return *this;
delete[] m_array;
m_size = rhs.m_size;
m_array = new T[m_size];
for (int i = 0; i < m_size; ++i)
m_array[i] = rhs.m_array[i];
return *this;
}
// 移动构造函数
DynamicArray(DynamicArray&& rhs) :
m_size{ rhs.m_size }, m_array{ rhs.m_array }
{
rhs.m_size = 0;
rhs.m_array = nullptr;
cout << "Move constructor: dynamic array is moved!\n";
}
// 移动赋值操作符
DynamicArray& operator=(DynamicArray&& rhs)
{
cout << "Move assignment operator is called\n";
if (this == &rhs)
return *this;
delete[] m_array;
m_size = rhs.m_size;
m_array = rhs.m_array;
rhs.m_size = 0;
rhs.m_array = nullptr;
return *this;
}
// 索引运算符
T& operator[](int index)
{
// 不进行边界检查
return m_array[index];
}
const T& operator[](int index) const
{
return m_array[index];
}
int size() const { return m_size; }
private:
T* m_array;
int m_size;
};
yun
// 生产int动态数组的工厂函数
DynamicArray<int> arrayFactor(int size)
{
DynamicArray<int> arr{ size };
return arr;
}
int main()
{
{
DynamicArray<int> arr = arrayFactor(10);
}
getchar();
return 0;
}
运行结果如下:
Constructor: dynamic array is created!
Move constructor: dynamic array is moved!
Destructor: dynamic array is destroyed!
Destructor: dynamic array is destroyed!
网友评论