左值与右值
在C++中,表达式可以分为左值和右值两种类型。左值是指表达式具有内存地址的对象,可以出现在赋值语句的左侧或者取地址符号的右侧。右值是指表达式不具有内存地址的临时对象,通常出现在赋值语句的右侧或者函数调用的实参中。
左值引用与右值引用
左值引用和右值引用则是用于引用左值和右值的两种引用类型。左值引用是对左值进行引用的类型,右值引用是对右值进行引用的类型。
具体来说,左值引用是指对一个左值进行引用的类型,它使用“&”符号进行定义。例如:
int n = 42;
int& ref = n; // 左值引用
int& a=42; // 直接报错,initial value of reference to non-const must be an lvalue
在这个例子中,我们定义了一个左值引用ref,它引用了一个左值n。由于n是一个左值,它具有内存地址,因此可以使用左值引用来引用它。
右值引用则是对一个右值进行引用的类型,它使用“&&”符号进行定义。例如:
int&& rref = 42; // 右值引用
在这个例子中,我们定义了一个右值引用rref,它引用了一个右值42。由于42是一个右值,它不具有内存地址,因此只能使用右值引用来引用它。
右值引用通常用于实现移动语义和完美转发等高级特性,可以提高程序的性能和可靠性。在C++11及以后的标准中,右值引用得到了广泛的应用,成为C++语言中的一个重要特性。
右值引用的应用1
std::move
std::move是右值引用的一种常用应用方式,
它是C++标准库中的一个函数模板,用于将一个对象转换为一个右值引用。
具体来说,std::move接受一个左值引用参数,返回一个右值引用,从而实现将左值转换为右值的效果。也就是说,std::move的功能就是把左值强制转化为右值,不会移动数据,单纯使用std::move不会提升效率。
简单的例子如
std::string str1 = "hello";
std::string str2 = std::move(str1); // 使用std::move进行转移
std::cout << str1 << std::endl; // 输出空
std::cout << str2 << std::endl; // 输出hello
使用std::move将一个左值引用str1转换为一个右值引用,然后将其传递给一个构造函数,从而实现了将一个对象的资源所有权从一个对象转移到另一个对象的效果,此时打印str1已经为空,更妙的是其中没有数据的移动和拷贝。
又比如对于unique_ptr,它的拷贝构造函数是被禁用的,即unique_ptr对象不能被拷贝。这是因为unique_ptr是一种独占式智能指针,它的特点是同一时间只能有一个指针可以访问所管理的对象,因此它不能被复制或者赋值,否则会导致多个指针同时指向同一块内存,从而破坏unique_ptr的独占性。
但unique_ptr提供了移动构造函数和移动赋值运算符,它们使用了移动语义来实现unique_ptr对象之间的内存资源转移。移动构造函数和移动赋值运算符可以确保只有一个unique_ptr对象能够管理所管理的内存,并且转移源对象的控制权给目标对象,从而保证unique_ptr的独占性。
例如
std::unique_ptr<int> p1(new int(42));
std::unique_ptr<int> p2(p1); // 错误,unique_ptr的拷贝构造函数被禁用
std::unique_ptr<int> p3 = p1; // 错误,unique_ptr的拷贝赋值运算符被禁用
std::unique_ptr<int> p4(std::move(p1)); // 正确,unique_ptr的独占性得到了保证,p1成为了空指针
更具体的例子参见参考链接,可以加深理解。
完美转发 std::forward
完美转发是指将函数参数以原样传递到另一个函数中,同时保留参数的值类别(左值或右值),从而实现对函数参数的完美转发。同样它也是只做类型转换,不会移动数据。
示例参见参考链接