[笔记] C++左右值、引用、移动语义
作者:互联网
准备
decltype()
decltype可获取一个变量的类型
int a = 5;
cout << typeid(decltype(a)).name() << endl; // "int"
decltype(a) b = 5; // 等价于 int b = 5;
左值、右值
表达式的定义:
An expression is a sequence of operators and operands that specifies a computation. An expression can result in a value and can cause side effects.
左值(lvalue)、右值(rvalue)以及一些更细的概(xvalue,glvalue,prvalue)的严格定义如下:
通俗来说,左值是在内存中(非代码段)占有空间的表达式。其余的表达式均为右值。
如:
// i 表示int变量, p表示指针
// 这些是左值
int i;
Foo o();
arr[5];
(p + 3)
{
int z = 0;
[&z]() { int& rz = z; return rz; }(); // 可以[&z]() { int& rz = z; return rz; }() = 5
}
//这些是右值
5
Foo(3);
i + 3 // 不能有&(i+3)
int k = []() {return 5; }();
注: 在C语言中,左值定义为可以放在赋值语句左边的表达式,右值则是右边
左值引用、右值引用
左值引用
在C++ 11中新增了右值引用,C++ 11之前的"引用"即现在的左值引用.
可以用is_lvalue_reference<T>()
来判断类型T
是否为左值引用。
int i = 5;
int& ref_i = i;
cout << is_lvalue_reference<int&>() << endl; // true
cout << is_lvalue_reference<decltype(ref_i)>() << endl; // true
语法糖
const int& i = 5;
等价为
const int five = 5;
const int& i = 5;
这在一些接收引用作为参数的函数中有用
void f(const string& str) {
}
f(string("Hello"));
// 如果没有这个语法糖,需要:
string x = "Hello";
f(x);
右值引用
对于右值,可以用auto&&
来引用
int&& i = 5;
任何引用都是左值
引用类型本质上是一级指针
应用:区分左值和右值
通过左值引用和右值引用,可以轻松判断传入的参数是左值还是右值
void f(int& x) {
cout << "lvalue " << x << endl;
}
void f(int&& x) {
cout << "rvalue " << x << endl;
}
f(5); // rvalue 5
int i = 5;
f(i); // lvalue 5
应用:拷贝构造函数和移动构造函数
再看一下智能指针的部分原理(Unique实现)
SimpleUniquePtr& operator=(const SimpleUniquePtr& rh) = delete; // disable copy assignment: this = rh
SimpleUniquePtr& operator=(SimpleUniquePtr&& rh) noexcept // move assignment: this = rh
{
if (this == &rh)
return *this;
delete this->ptr;
this->ptr = rh.ptr;
rh.ptr = nullptr; // 防止rh的资源被delete
return *this;
}
智能指针在被赋值时,根据被赋的值有两种截然不同的反应: 如果是右值,则夺走对方的指针;如果是左值,则这种行为会引发错误
// 不能这么做!因为智能指针是Unique
auto str = new char[8] {"Hello"};
auto ptr1 = SimpleUniquePtr(str);
auto ptr2 = ptr1; // ptr1是左值
// 可以这么做: 把原来的资源丢掉,绑定到一个新建的资源
auto str = new char[8] {"Hello"};
auto str2 = new char[8]{ "World" };
auto ptr2 = SimpleUniquePtr(str);
// SimpleUniquePtr(str)在这里被释放
auto ptr2 = SimpleUniquePtr(str2); // ptr2是右值(xvalue)
// SimpleUniquePtr(str2)在这里被释放,ptr2留存
引用类型转换:移动语义
move
是一个类似于强制类型转换的东西,它可以把左值(和右值)引用转化为右值引用。他本身没有任何内存上的实质作用,更多的和拷贝构造函数和移动构造函数一起使用
他的源码也较为简单,就是移除引用然后再变为右值引用。
constexpr auto&& move(T&& arg) {
return static_cast<remove_reference_t<T>&&>(arg);
}
int x = 5;
cout << is_lvalue_reference<decltype(move(x))>() << endl; // false (右值)
例:
char* str0 = new char[10]{ "Hello" };
char* str1 = new char[10]{ "World" };
auto ptr0 = SimpleUniquePtr(str0);
auto ptr1 = SimpleUniquePtr(str0);
SimpleUniquePtr ptr2 = ptr1; // 触发operator=(auto&) = delete
SimpleUniquePtr ptr2 = move(ptr1); // 触发operator=(auto&&), 成功转移所有权
// ptr1的指针被夺走
dbg(ptr1.isNull());
引用的引用
左引用的左引用是左引用 (int&)& == int&
右引用的右引用是右引用 (int&&)&& == int&&
左右引用混合是左引用 (int&)&& == (int&&)& = int&
应用:万能引用
template<typename T>
void f(T&& x) {
cout << is_lvalue_reference<decltype(x)>() << endl;
}
f(3); // false (是右引用) 传入 int&& => (int&&)&& == int&&
int i = 3;
f(i); // true (是左引用) 传入 int& => (int&)&& == int&
参考资料
[1]ISO.Working Draft, Standard for Programming Language C++ N4861.https://isocpp.org/std/the-standard
标签:右值,int,左值,SimpleUniquePtr,语义,笔记,C++,&&,引用 来源: https://www.cnblogs.com/winterreisender/p/15926675.html