学习c++的右值引用
文章在此
虽然,purecpp.org
更新不怎么频繁,但其原来的内容
,还是有很多宝贝的.只是有些 排版
,实在不是很好.不过,学习为主
.
上一篇是d的右值引用
.这一篇是c++
的.
这是最后的结论:
实现就是:静转<T&&>,将T/T&/T&&转为T&&,这样就是右值引用了.右值引用就是个废物利用.
.如果看懂了,就不必再看了.
1,左值引用:整&i=j;
.
2,右值引用:整&&j=22;
右值引用解决的是:临时对象不必要的复制操作
及模板参数按实际类型转发(完美转发)
.
右值引用相关概念:右值
,纯右值
,将亡值
,一般引用(universal...)
,引用折叠
,移动语义
,完美转发
.
四行学右值引用
第一行:int i = getVar();
这个getvar()
变量返回临时值
,在表达式结束后就没了
,这个值就是纯右值
的右值
.能对表达式取地址
,即为左值
,否则为右值
.
具名变量
都是左值
.字面量都是右值
.右值不具名.
右值
是表达式结束后
就不存在的临时对象
.
值=左值+右值
,右值=将亡值+纯右值
,
纯右值
:非引用返回的临时变量
,表达式产生的临时变量
,字面量
,入表达式(注意这个)
.
将亡值
:与右值引用相关的表达式
:要被移动的对象
,T&&函数返回值
,移动返回值
,转换为T&&的函数返回值
.
第2行,T&&k=getVar();
右值是匿名的
,只能通过&&引用
,此临时值通过&&续命
,和k生命期一样长.
代码如下:
#include <iostream>
using namespace std;
int g_constructCount=0;
int g_copyConstructCount=0;
int g_destructCount=0;
struct A
{
A(){
cout<<"construct: "<<++g_constructCount<<endl;
}
A(const A& a)
{
cout<<"copy construct: "<<++g_copyConstructCount <<endl;
}
~A()
{
cout<<"destruct: "<<++g_destructCount<<endl;
}
};
A GetA()
{
return A();//这里(构造一次,拷贝构造一次)
}
int main() {
A a = GetA();//(复制构造),第一次
A&&a = GetA();//第二次,改成右值引用
//也可以
//常量左值引用
const A& a = GetA();//
return 0;
}
暂时这样.想把csdn的上面头给去掉下固定.结果不行.下面又有个遮罩了.
搞不了.
优化后,但不是标准.
因为常量左值引用是一个“万能”的引用类型,可以接受左值、右值、常量左值和常量右值。
但不能这样A& a = GetA();
,因为非常量左值
不能引用右值
第一个特点:延长寿命
,本来是个临时值,
,寿命延长.
第二个特点:右值引用独立于左值和右值。意思是右值引用类型的变量可能是左值也可能是右值
int&& var1 = 1;
,变量1类型为右值引用
,但本身是左值
template<typename T>
void f(T&& t);
f(10); //t是右值
int x = 10;
f(x); //t是左值
T&&表示的值类型不确定,可能是左值又可能是右值,右值引用的特点。
第三个特点:
T&& t在发生自动类型推断的时候,它是未定的一般引用(universal references),如果被左值初始化,它就是一个左值;如果它被一个右值初始化,它就是一个右值,它是左值还是右值取决于它的初始化。
仅当发生自动类型推导时才是.如下:
template<typename T>
void f(T&& param); //一般引用,要发生参数推导.
template<typename T>
class Test {
Test(Test&& rhs); //右值引用.这是确定类型.主要用于移动.
};
引用折叠:
&&+&&=&&
其余:&+&
,&+&&
,&&+&
,=>都是&
.
第三行T(T&&a):m_val(val){ a.m_val=nullptr;}
移动语义来了.将右值引用作为构造函数的参数
.
class A
{
public:
A():m_ptr(new int(0)){cout << "construct" << endl;}
A(const A& a):m_ptr(new int(*a.m_ptr)) //深拷贝的拷贝构造函数
{
cout << "copy construct" << endl;
}
A(A&& a) :m_ptr(a.m_ptr)//`是确定的右值引用类型`
{//增加一个移动语义,调用移动构造,就没必要再生成,再释放临时对象了.
a.m_ptr = nullptr;
cout << "move construct" << endl;
}//构造函数只能接受右值参数,而函数返回值是右值,就匹配到这个构造函数。
~A(){ delete m_ptr;}
private:
int* m_ptr;
};
int main() {
A a = GetA();
return 0;
}
输出:
construct
copy construct
copy construct
带堆内存的类(必须要有深拷贝构造),默认为浅拷贝,但深拷贝有时,没必要.如果没(深拷贝),会产生悬挂
指针.一是临时右值析构,二外面的a对象析构
.这个时候,就可以移动语义,将所有权转义给a.
移动不行时,还得有构造函数做为基础
对普通左值
进行优化,也可以移动
{
std::list< std::string> tokens;
//省略初始化...
std::list< std::string> t = tokens; //这里存在拷贝
}
std::list< std::string> tokens;
std::list< std::string> t = std::move(tokens); //这里没有拷贝
移动
将左值转换为右值引用
,变成临时值,避免拷贝,提高性能
move对于含资源(堆内存或句柄)的对象来说更有意义。
第四行,完美转发,template void f(T&& val){ foo(std::forward(val)); }
template <typename T>
void forwardValue(T& val)
{
processValue(val); //右值参数会变成左值
}
template <typename T>
void forwardValue(const T& val)
{
processValue(val); //参数都变成常量左值引用了
}
//可以了.
void processValue(int& a){ cout << "lvalue" << endl; }
void processValue(int&& a){ cout << "rvalue" << endl; }
template <typename T>
void forwardValue(T&& val)
{
processValue(std::forward<T>(val)); //照参数本来的类型进行转发。
}
void Testdelcl()
{
int i = 0;
forwardValue(i); //传入左值
forwardValue(0);//传入右值
}
输出:
lvaue
rvalue
结合完美转发,创建工厂函数
.
template<typename... Args>
T* Instance(Args&&... args)
{
return new T(std::forward<Args >(args)...);
}
总之右值引用,就是引用个没有名字的临时变量
.右值引用==临时变量
.
用来合理利用已产生的废物的
.
官方结语:完美转发
,移动语义
,优化性能
实现就是:静转<T&&>
,将T/T&/T&&
转为T&&
,这样就是右值引用
了.右值引用就是个废物利用
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现