【C++】C++11------右值引用

右值引用概念

C++98中提出了引用的概念,引用即别名,引用变量与其引用实体公共同一块内存空间,而引用的底层是通过指针来实现的,因此使用引用,可以提高程序的可读性。

为了提高程序运行效率,C++11中引入了右值引用,右值引用也是别名,但其只能对右值引用。

int fun(int n)
{
    
    
    return n - 1;
}
 
int main()
{
    
    
    int x = 1, y = 2;
    int&& a = 10; // 引用常量
    int&& b = x + y; // 引用表达式
    int&& c = fun(2); // 引用函数返回值
    return 0;
}

右值和左值概念

左值与右值是C语言中的概念,但C标准并没有给出严格的区分方式,一般认为:可以放在=左边的,或者能够取地址的称为左值,只能放在=右边的,或者不能取地址的称为右值,但是也不一定完全正确。

右值和左值的区别

1.普通类型的变量,因为有名字,可以取地址,都认为是左值。
2.const修饰的常量,不可修改,只读的理论应该按照右值对待,但因为其可以取地址(如果只是const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间),C++11认为其是左值。
3.如果表达式的运行结果是一个临时变量或者对象,认为是右值。
4.如果表达式运行结果或单个变量是一个引用则认为是左值。

左值引用和右值引用

在这里插入图片描述


编译器报错,说明左值引用不能直接引用右值,右值不能直接引用左值。
普通引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值。C++11中右值引用:只能引用右值,一般情况不能直接引用左值,可以通过move将左值变成右值然后引用。

在这里插入图片描述

左值引用的不足

当我们在函数中使用引用传传参时几乎是没有任何问题的,但是当函数中的返回值使用引用返回时可能就会出现问题。

比如:我们使用string的实现作为例子

以前在学习拷贝构造时我们学习过,如果返回值是一个自定义类型,由于返回的是一个右值,所以在处理该函数的作用域之后,该右值会被立即销毁。所以在返回之前,会调用一次拷贝构造将返回值临时保存在一个临时变量中,临时变量再拷贝构造或构造赋值给自定义类型 ,这样会有很大的损耗,在C++98中,编译器为了减少这种损耗,会将 构造 + 拷贝构造 连续两次的构造优化为一次构造。

移动构造
将亡值:马上要进行释放的变量
移动构造和移动赋值就是将要释放的资源,转移到准备新构造的资源,从而达到资源转移的作用,省去志愿的释放和申请。
有了移动赋值我们也可以将左值手动将左值move为右值,不过move后左值的资源会被转移走。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

移动构造解决返回值是局部自定义类型问题
在这里插入图片描述

完美转发

完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。

万能引用:模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力

void Fun(int& x) {
    
     cout << "左值引用" << endl; }
void Fun(const int& x) {
    
     cout << "const 左值引用" << endl; }
 
void Fun(int&& x) {
    
     cout << "右值引用" << endl; }
void Fun(const int&& x) {
    
     cout << "const 右值引用" << endl; }


template<typename T>
void PerfectForward(T&& t)  //万能引用,技能接受左值引用(折叠),也能接收右值引用
{
    
    
    Fun(t);
}
 
int main()
{
    
    
    PerfectForward(10);           // 右值
 
    int a;
    PerfectForward(a);            // 左值
    PerfectForward(std::move(a)); // 右值
 
    const int b = 8;
    PerfectForward(b);              // const 左值
    PerfectForward(std::move(b)); // const 右值
 
    return 0;
}

在这里插入图片描述

上述代码中,我们传入PerfectForward函数中既有左值,又有右值,为什么在他的函数中使用传入的值调用别的函数的参数类型是左值。
其实我们在前面使用过程中可以知道,在使用右值引用转移资源后,他的属性会变成左值,有自己的地址且可以改变了,我们可以理解为在使用右值引用是创建了一个新的空间区保存他。

Fun(std::forward<T>(t)); //forward<T> 完美转发:将参数按照传递给转发函数的实际类型转给目标函数,而不产生额外的开销

所谓完美:函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相应实参是右值,它就应该被转发为右值。

在这里插入图片描述

在这里插入图片描述

对传入的参数进行forward,保持他的类型。

猜你喜欢

转载自blog.csdn.net/Tianzhenchuan/article/details/133390113
今日推荐