STL源码:准备知识(右值引用、forward转发、变参数模板)

本文详细解析了C++中的右值引用和完美转发机制,包括其原理、应用场景及注意事项。并通过实例展示了如何利用move和forward来提高代码效率。

QT(5.8版本)标准库源码主要在安装目录下的(VC找inlcude目录)
在这里插入图片描述
扩充内容的源码在
在这里插入图片描述

OOP 和 GP

oop(面向对象编程):Object-Oriented Programming,数据和操作放在一起(同一个类中)
GP(泛型编程):Generic Programming,数据和操作分开

使用GP:
Container和Algorithms团队可各自设计,其间以Iterators沟通即可
Algorithms通过Iterators确定操作范围,并通过Iterators取用Container元素
在这里插入图片描述
举例之:sort

源文件:stl_algo.h
有两个函数,分别是自定义从小到大版本,和允许接收仿函数自定义排序规则版本,两者都调用底层的 __sort函数
在这里插入图片描述
__sort是底层实现,重点在于对introsort_loop的调用,关于它的写法不同版本可能有些许不同,但重点在于它必须是可以随机访问的迭代器,即RandomAccessIterator,否则无法实现 *2(或/2)的操作,这也是为什么list不能使用::sort()的原因
在这里插入图片描述

c++ forward转发

在学习源码之前,需要先了解c++中的forward转发的用法

非常好的一篇:https://www.cnblogs.com/kaleidopink/p/13720773.html

右值引用原理:https://blog.youkuaiyun.com/m0_54850825/article/details/124761539

背景

背景:在C++11出现之前,C++传值默认是copy,但copy开销很大
举例:(a = b + c + d)中,c+d是一个临时变量, b+(c+d)又是另一个临时变量
右值:这些临时变量在C++11中被定义为右值(rvalue,read value),右值没有相应的变量名存储它们
左值:与右值相对的是左值(lvalue,localtor value),左值有变量名

场景:

class A {
   
   ...};
A a;
a.set("temp");//将"temp"复制给a的成员变量

可能的底层步骤:
临时变量"temp"在传参时先被复制一遍
被复制的内容放到a的成员变量中去
回收临时变量

可以改进的地方:临时变量既然是要回收的,那么可以直接使成员变量接管临时变量,就可以避免中间的复制过程
move和forward就是为此而产生的

左值引用和右值引用

左值引用

1. 原理

关于引用:&b=a实际上等价于int* const b=&a,而编译器会把&b编译为:&(*b)
它的实现是:将a的地址放到寄存器中,然后再将寄存器中的地址传给引用变量b

常量之所以可以左值引用,是因为它被放在静态存储区,是有地址的。而像是10这样的临时变量是放在寄存器中的,无法取址

2. 使用和注意事项

左值引用要求右值必须能够取地址,如果不能取地址,则必须为常引用。因为左值引用本质上是将地址赋给左值

int &b = a + 1;//错误,左值引用必须能够取地址

a+1不能被认为一个在内存中存在地址的变量,如果要这样用,需要加上const修饰。

同样的函数返回值不能使用左值引用,例如 int &a = func();这种情况要么修改为常引用或者不使用引用,但常引用会导致引用不可修改。

普通引用只能引用左值,不能引用右值,const引用既可引用左值,也可引用右值

当使用&b=a; 对b的操作相当于对(*b)的操作,即对a的操作

右值引用

1. 原理

int&& ref = 3;

临时变量(编译时未分配内存或使用寄存器存放的值)引用关联到右值时,右值被存储到特定位置
它的汇编指令和const &b = 3是一样的,只是后者不允许修改

用临时变量将3存起来,然后将地址送入寄存器;寄存器再将地址传给ref

变量和临时变量的值实际是按同一方式处理的,也就是说,临时变量根本上来说就是一个没有名字的变量而已,从汇编的角度来说,都是指针

C++引入右值引用,是为了延长临时变量的生命周期,避免昂贵的copy开销。相当于用指针直接接管这一块内存,而不是重新申请内存再复制

2. 使用和注意事项

右值引用可以处理临时变量的情况,即无法寻址的情况

类型 &&引用名 = 右值表达式;//定义格式

右值引用可以延长临时变量的生存周期,避免了无谓的内存复制操作。

C++11中右值引用:只能引用右值,一般情况不能直接引用左值

单纯的右值引用没有意义,它常与move和forward一起使用

引用叠加

int x = 1;
int&& r1 = x;//实际上这一步就会报错了
auto&& r2 = r1;

r2最终是一个左值引用
所有的右值引用叠加到右值引用上仍然是一个右值引用;
所有其他类型之间的叠加将会使得变成一个左值引用

move移动语义

参考:https://blog.youkuaiyun.com/u013015629/article/details/116525759

move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转义,没有内存拷贝。

int d = 1;
int &&r = d;

上述操作是不被允许的,如果需要用右值引用接收左值,可以采用move函数(当不确定接收的是左值还是右值时可以这样使用)

int &&r = std::move(d);

而且move几乎没有代价,只是转移了资源的控制权。尽量可以用。

  • 如果没有提供移动构造函数,只提供了拷贝构造函数,std::move()会失效但是不会发生错误,因为编译器找不到移动构造函数就去寻找拷贝构造函数,也这是拷贝构造函数的参数是const T&常量左值引用的原因
  • c++11中的所有容器都实现了move语义,move只是转移了资源的控制权,本质上是将左值强制转化为右值使用,以用于移动拷贝或赋值,避免对含有资源的对象发生无谓的拷贝。move对于拥有如内存、文件句柄等资源的成员的对象有效,如果是一些基本类型,如int和char[10]数组等,如果使用move,仍会发生拷贝(因为没有对应的移动构造函数),所以说move对含有资源的对象说更有意义。

forward完美转发

move会将左值强制变成右值使用,而forward会保持原变量的属性
若对一个对象做move操作,就不要声明为 const. 因为对const对象的move请求会执行到copy操作上

void func(int&& i) {
   
   
    cout << "rvalue: " << i << endl;
}
void func(int& i) {
   
   
    cout << "lvalue: " << i << endl;
}


int main() {
   
   
    int val = 2;
    int &m1 = val;
    //在不使用move或者forward时
    func(m1);//lvalue
    func(3);//rvalue
    //使用move会将左值变成右值
    func(std::move(m1));//rvalue
    func(std::move(3
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值