C++11:右值引用,实现移动语义和完美转发

1、右值引用

右值引用(Rvalue reference)是C++11引入的一个新特性,它是一种新的引用类型,用于表示将要被移动的对象或临时对象。

(1)首先了解几个关键名词:

(1)左值:可以取地址的表达式,并且有名字; (2)右值:不能取地址的表达式,且没有名字; (3)纯右值:运算表达式产生的临时变量,不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式都是纯右值; (4)将忘值:即将销毁的值; (5)左值引用&:对左值进行引用的类型,等号右边的值必须可以取地址 (6)右值引用&&:对右值进行引用的类型,等号右边的值需要是右值,可以使用std::move函数强制把左值转换为右值。

(2)相关的代码展示:

int a = 1;  // a是左值
int b = a;  // a是左值,b是左值
int c = a + b;  // a和b是右值,c是左值
int&& rvalue_ref = 1;  // rvalue_ref是右值引用

int &&d = a; // error, a是左值
int &&e = std::move(a); // ok

2、移动语义(std::move)

(1)移动语义含义:转移资源所有权,类似转让或者资源窃取,对于那块资源,转为自己所拥有,别人不再拥有也不会再使用;理解深浅拷贝,直接把原来需要拷贝的内存易主

在C++11之前,我们拥有4个特殊成员函数,即构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。从C++11开始,我们多了2个特殊成员函数,即移动构造函数和移动赋值运算符。

在C++11之后,如果我们定义一个空类,除了之前的4个特殊成员函数,编译器还会为我们生成移动构造函数和移动赋值运算符:

但是我们自定义一些函数时候,可能就不一定会自动生成移动构造函数和移动赋值运算符,相关的关系看下面的表:

(2)右值引用的主要作用是实现移动语义(Move Semantics),即在对象的拷贝或赋值操作中,将资源的所有权从一个对象转移到另一个对象,避免不必要的拷贝和内存分配。例如:

// 例子1
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1);  // 使用右值引用实现移动


// 例子2
std::vector<string> vecs;
...
std::vector<string> vecm = std::move(vecs); // 免

//类的
int main() 
{
	A a(10);
	A b = a;
	A c = std::move(a); // 调用移动构造函数
	return 0;
}

例子1:在上面的代码中,使用std::move将vec1转换为右值引用,然后将其赋值给vec2。由于vec1已经成为右值,因此可以安全地将其资源(即动态分配的内存)转移给vec2,避免了不必要的拷贝和内存分配。

注意:移动语义仅针对于那些实现了移动构造函数的类的对象,对于那种基本类型int、float等没有任何优化作用,还是会拷贝,因为它们实现没有对应的移动构造函数。

(2)使用 std::forward 的示例:

#include <iostream>
#include <utility>

template<typename T>
void print(T&& arg) {
    std::cout << "Printing: " << std::forward<T>(arg) << std::endl;
}

int main() {
    int a = 5;
    const int b = 10;
    print(a); // T is int&, arg is int&
    print(b); // T is const int&, arg is const int&
    print(15); // T is int, arg is int&&
    return 0;
}

在 main 函数中,我们分别调用 print 函数,传递了一个 int 类型的变量 a,一个 const int 类型的变量 b,以及一个 int 字面量 15。当我们传递 a 和 b 时,T 的类型分别为 int& 和 const int&,因此 arg 的类型也分别为 int& 和 const int&。当我们传递 15 时,T 的类型为 int,arg 的类型为 int&&。

经验分享 程序员 微信小程序 职场和发展