[笔记] C++左右值、引用、移动语义
2022/2/23 14:24:45
本文主要是介绍[笔记] 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
这篇关于[笔记] C++左右值、引用、移动语义的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-04-26高性能表格工具VTable总体构成-icode9专业技术文章分享
- 2024-04-16软路由代理问题, tg 无法代理问题-icode9专业技术文章分享
- 2024-04-16程序猿用什么锅-icode9专业技术文章分享
- 2024-04-16自建 NAS 的方案-icode9专业技术文章分享
- 2024-04-14ansible 在远程主机上执行脚本,并传入参数-icode9专业技术文章分享
- 2024-04-14ansible 在远程主机上执行脚本,并传入参数, 加上remote_src: yes 配置-icode9专业技术文章分享
- 2024-04-14ansible 检测远程主机的8080端口,如果关闭,则echo 进程已关闭-icode9专业技术文章分享
- 2024-04-14result 成功怎么写-icode9专业技术文章分享
- 2024-04-14stopped 状态设置为变量,由外部传递进来-icode9专业技术文章分享
- 2024-04-14为什么ansible执行远程脚本需要放到后台-icode9专业技术文章分享