左值 | 左值引用 | 右值 | 右值引用 | |
---|---|---|---|---|
定义 | 可以在等号左边,能够取地址,有具体名称 | 对左值的引用 | 只能在等会右边,不能取地址,没有具体名字 | 对右值的引用 |
功能 | 函数传参、函数返回值 | 实现移动语义、实现完美转发 | ||
举例 | 见下文 |
左值举例:
变量名
int a;
返回左值引用的函数调用
MyClass& Create();
前置自增/自减
++ i;
++ i = 100; // 正确
赋值运算、复合赋值运算
int a = 0;
a = 90;
(a += 9) =100; // 复合
解引用
ClassA* pA = new ClassA;
*pA;//解引用
右值举例
右值分为纯右值、将亡值
纯右值
字面值
int a = 10; // 10为纯右值
返回非引用类型的函数调用
class Test
{
// ...
};
// Create()为纯右值
Test Create()
{
Test t;
return t;
}
后置自增/自减
int i = 0;
i ++;
i ++ = 109; // 编译报错
表达式
int a = 0;
int b = 0;
a - b; // 算术表达式
a | b; // 逻辑表达式
a != b; // 比较表达式
将亡值
C++11新引入的类型
与右值引用(移动语义)相关的功能
class MyClass
{
//...
MyClass(MyClass &&)
{
std::cout<<"移动构造"<<std::endl;
}
MyClass& operator=(MyClass &&)
{
std::cout<<"移动赋值构造"<<std::endl;
}
};
MyClass Create()
{
MyClass t;
return t;
}
// MyClass(MyClass &&)存在,则Create()调用移动构造,为将亡值
// MyClass(MyClass &&)不存在,则Create()调用移动赋值构造,为纯右值
将亡值用于触发移动构造或移动赋值构造,并进行资源转移,之后将调用析构函数
引用
别名
声明时必须要初始化
通过引用修改变量值
int && j = 100; // j为右值引用
- cosnt 左值引用可以引用右值,但不能修改这个值
- 右值引用通过std::move()可以指向左值
- 声明出来的左右值引用都是左值
实现移动语义
对象赋值时,避免资源(堆、连接、文件)的重新分配(针对深拷贝场景)
通过触发移动构造以及移动拷贝构造
class A
{
public:
A(const A& a)
{
ptr = new char;
memcpy(ptr, a.ptr, sizeof(char));
std::cout<<"拷贝构造"<<std::endl;
}
A(A&& a)
{
this->ptr =a.ptr;
a.ptr = nullptr;
std::cout<<"移动构造"<<std::endl;
}
char* ptr;
};
A a,b;
a = b;
A b;
A a(b); // 调用A(const A& a)
A a(std::move(b)); // 调用A(A&& a)
stl应用
class A
{
// ...
};
std::list<A> alist;
alist.push_back(A()); // 移动构造 C++11 开始支持
完美转发
函数模板可以将自己的参数完美的转发给内部调用的其他函数
完美:不仅能转发值,还能保证转发值的属性(左值、右值)不变
void func(int & n)
{
std::cout<<"lvalue="<<n<<std::endl;
}
void func(int &&n)
{
std::cout<<"rvalue="<<n<<std::endl;
}
template<typename T>
void revoke(T &&t) // 万能引用 通过引用的方式接收左右属性的值
{
func(std::forward<T>(t));
}
int &m = 100;
int &&n = 100;
revoke(static_cast<int&>(m)); // lvalue=
revoke(static_cast<int&&>(n)); // rvalue=
revoke(m); // lvalue=
revoke(n); // lvalue=
万能引用: T&& auto&& 具体类&&并不是
引用折叠规则:
- 参数为左值或左值引用,T&& 将转化为int&
- 参数为右值或右值引用,T&&将转化为int&&
revoke(static_cast<int&> n);
// int & && -> int&
revoke(static_cast<int&&> m);
// int && && -> int&&
revoke(n);
// int && -> int&
std::forward(v):
- T为左值引用,v将转化为T类型的左值
- T为右值引用,v将转化为T类型的右值