当前位置: 首页 > wzjs >正文

高唐做网站建设公司东莞推广

高唐做网站建设公司,东莞推广,想要去国外网站买东西怎么做,上海人才网最新招聘信息2022年文章目录 右值引用和移动语义在传参中的提效list容器push_back & insert右值版本的模拟实现类型分类 (了解即可)引用折叠万能引用 完美转发(跟引用折叠有关) 简介:这篇文章是继续介绍C11的一些新语法知识点,也是对…

文章目录

  • 右值引用和移动语义在传参中的提效
  • list容器`push_back` & `insert`右值版本的模拟实现
  • 类型分类 (了解即可)
  • 引用折叠
    • 万能引用
  • 完美转发(跟引用折叠有关)

简介:这篇文章是继续介绍C++11的一些新语法知识点,也是对C++11(1)的补充和延续

右值引用和移动语义在传参中的提效

下面是list容器insertpush_back接口实现的右值传参的函数重载,当传入左值时,会去拷贝构造一份该左值插入到容器list中。当传入右值时,会去直接转移该右值的资源,再插入到容器list中,因此便起到了右值引用在传参中的提效只不过要特别注意的一点是,插入的值如果是容器的话,那得支持移动构造

在这里插入图片描述
在这里插入图片描述

// 这个string容器是咱自己实现的,不是库里的string。这样才能看清是拷贝构造,还是移动构造
int main()
{std::list<xiao::string> lt;xiao::string s1("111111111111111111111");lt.push_back(s1);cout << "*************************" << endl;lt.push_back(xiao::string("22222222222222222222222222222"));cout << "*************************" << endl;lt.push_back("3333333333333333333333333333");cout << "*************************" << endl;lt.push_back(move(s1));cout << "*************************" << endl;return 0;
}

在这里插入图片描述

list容器push_back & insert右值版本的模拟实现

这里为了方便更好展现两接口右值版本的模拟实现,需要用到自己实现的list容器,毕竟不只是牵扯到这两个函数这么简单,倒是有点牵一发而动全身的意思下面的list容器只是为了去配合右值版本的模拟实现,并不完整,但够用了

// 没有重载右值版本的list容器
namespace xiao
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr), _prev(nullptr), _data(data){}};template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}Self& operator++(){_node = _node->_next;return *this;}Ref operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}};template<class T>class list{typedef ListNode<T> Node;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}void empty_init(){_head = new Node();_head->_next = _head;_head->_prev = _head;}list(){empty_init();}void push_back(const T& x){insert(end(), x);}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}private:Node* _head;};
}

这里new出的一个哨兵位结点可以看这个函数empty_init() ,其次为啥这里的析构与上面那副图片的析构不对等?仅仅只是这个list没去写析构函数,这里为了方便阐述右值版本的模拟实现就没写了,但在实际开发中要特别注意,否则会造成严重的资源泄漏

在这里插入图片描述

这里push_back传参是右值的话,就会匹配下面的这个函数,但是之前咱们讲过,这个x它是具有左值属性的,那就不会走下面右值版本的insert,所以需要将这个x进行强转move(x),但当你运行程序后发现还是拷贝构造,是因为insert中的x又退化到了左值,所以需要对这个new Node(x)中的x再进行强转move(x)使它传给下一层是右值

void push_back(T&& x)
{insert(end(), x);// insert(end(),move(x));
}
iterator insert(iterator pos, T&& x)
{Node* cur = pos._node;Node* newnode = new Node(x);//Node* newnode = new Node(move(x));Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}

但当你执行程序后,仍然是拷贝构造。这也太多坑了吧。这是因为传入的这个右值,结点没有相应的右值构造,仍然走的是const左值引用构造函数。OK,那我再重载一个右值构造函数,但仍然是拷贝构造函数,因为这个_data(data)中的data有退化到左值了,所以需要move(data)

ListNode(const T& data = T()):_next(nullptr), _prev(nullptr), _data(data)
{}
// 这里重载的结点构造函数不能再给缺省值了
// 全缺省的是默认构造,但默认构造函数只能有一个
ListNode(T&& data):_next(nullptr), _prev(nullptr), _data(data)// _data(move(data))
{}

这样一套操作下来就完成了右值引用版本的push_back与insert接口的重载。接下来咱用流程图来清晰的展示向下传值的过程

在这里插入图片描述
在这里插入图片描述

类型分类 (了解即可)

  • C++11以后,进⼀步对类型进⾏了划分,右值被划分纯右值(pure value,简称prvalue)和将亡值(expiring value,简称xvalue)。
  • 纯右值是指那些字⾯值常量或求值结果相当于字⾯值或是⼀个不具名的临时对象。如: 42、true、nullptr 或者类似 str.substr(1, 2),str1 + str2 传值返回函数调⽤,或者整形 a、b,a++,a+b 等。纯右值和将亡值C++11中提出的,C++11中的纯右值概念划分等价C++98中的右值。
  • 将亡值是指返回右值引⽤的函数的调⽤表达式和转换为右值引⽤的转换函数的调⽤表达,如move(x)、static_cast<X&&>(x)
  • 泛左值(generalized value,简称glvalue),泛左值包含将亡值和左值
  • 值类别 - cppreference.com 和 Value categories这两个关于值类型的中⽂和英⽂的官⽅⽂档,有兴趣可以了解细节。

引用折叠

C++中不能直接定义引⽤的引⽤如 int& && r = i; ,这样写会直接报错,通过模板或 typedef中的类型操作可以构成引用的引用。

template<class T>
void f2(T&& x)
{cout << &x << endl;
}int main()
{// 直接对引用的引用会报错int&& a = 2;//int&&& b = a; 是不允许对引用的引用typedef int&& ref; // using ref = int&&;ref& b = a;cout << &b << endl;cout << &a << endl;f2(a); // 打印出来的三个地址是相同的
}

通过模板或 typedef 中的类型操作可以构成引用的引用时,这时C++11给出了⼀个引用折叠的规则:右值引用的右值引用折叠成右值引用,所有其他组合均折叠成左值引用一共具有四种组合,没有为什么,这么去规定是 为了契合某些语法

int main()
{typedef int& lref;typedef int&& rref;int n = 0;lref& r1 = n; // r1 的类型是 int&lref&& r2 = n; // r2 的类型是 int&rref& r3 = n; // r3 的类型是 int&rref&& r4 = 1; // r4 的类型是 int&&return 0;
}

通过这段代码可以去验证,左值引用的其它引用(左值引用或右值引用)永远会被折叠成左值引用,右值引用的右值引用才会被折叠成右值引用

// 由于引⽤折叠限定,f1实例化以后总是⼀个左值引⽤
template<class T>
void f1(T& x)
{}// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用
template<class T>
void f2(T&& x)
{}int main()
{// 没有折叠->实例化为void f1(int& x)f1<int>(n);f1<int>(0); // 报错// 折叠->实例化为void f1(int& x)f1<int&>(n);f1<int&>(0); // 报错// 折叠->实例化为void f1(int& x)f1<int&&>(n);f1<int&&>(0); // 报错// 折叠->实例化为void f1(const int& x)f1<const int&>(n);f1<const int&>(0);// 折叠->实例化为void f1(const int& x)f1<const int&&>(n);f1<const int&&>(0);// 没有折叠->实例化为void f2(int&& x)f2<int>(n); // 报错f2<int>(0);// 折叠->实例化为void f2(int& x)f2<int&>(n);f2<int&>(0); // 报错// 折叠->实例化为void f2(int&& x)f2<int&&>(n); // 报错f2<int&&>(0);return 0;
}

万能引用

如果该模板函数的参数是右值引用,即template<class T> void f2(T&& x)这个和之前的那个实例化的函数参数的右值引用是不一样的,之前实例化的函数参数x的类型它是确定的,比如(int/double && x)。而模板函数它的类型是不确定的,它会去推导,而又因为引用折叠的规则,就会导致传递左值的时候函数参数是左值引用,传递右值的时候函数参数是右值引用,因此也把这种函数模板的参数称为万能引用

在这里插入图片描述

这里可以这么去理解T推导出来的类型,传参时Function(value)如果value是左值的话,那Function的形参它得是 int t 或者 int& t,那这个T会被推导成啥类型才能配合T&&引用折叠成出上面两种类型呢?value是右值的话,那Function的形参必须得是int&& t,T可能会被推导出int或者int &&,具体是哪种得看函数内部的实现,下面就有个特殊的例子

template<class T>
void Function(T&& t)
{int a = 0;T x = a;x++;cout << &a << endl;cout << &x << endl << endl;
}
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// b是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int&t)// 所以Function内部会编译报错,x不能++Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)// 所以Function内部会编译报错,x不能++Function(std::move(b)); // const 右值return 0;
}

下面的这段代码可能会存在疑问,为啥这个T推导出来是int而不是int &&呢?T &&,当T为int &&的时候照样会被折叠成右值引用啊(右值引用的右值引用才会被折叠成右值引用)。是没问题的,但载函数中 int a = 0; T x = a;当T为int &&时,变量x无法去作变量a的别名,因为a是一个左值

// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)
Function(std::move(a)); // 右值

完美转发(跟引用折叠有关)

完美转发是什么意思?为啥跟引用折叠有关?需要去解决一个什么样的问题

  1. 完美转发它本质上就是一个函数模板,forward
lvalue (1)	
template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
rvalue (2)	
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
  1. 为啥跟引用折叠有关,需要去解决一个什么样的问题
void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引⽤" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }template<class T>
void Function(T&& t)
{Fun(t);
}
int main()
{// 10是右值,推导出T为int,模板实例化为void Function(int&& t)Function(10); // 右值int a;// a是左值,推导出T为int&,引⽤折叠,模板实例化为void Function(int& t)Function(a); // 左值// std::move(a)是右值,推导出T为int,模板实例化为void Function(int&& t)Function(std::move(a)); // 右值const int b = 8;// a是左值,推导出T为const int&,引⽤折叠,模板实例化为void Function(const int&t)Function(b); // const 左值// std::move(b)右值,推导出T为const int,模板实例化为void Function(const int&&t)Function(std::move(b)); // const 右值return 0;
}

上面的代码它重载了四个Fun函数,我们在main函数中往Function函数中传入左值或右值,那该模板函数就会发生引用折叠,此时参数t就会产生不同的类型(int&,int&&,const int&等)。那就需要各自类型的参数t去匹配不同的Fun函数,可是当我们去运行代码后,发现咱都是跟左值引用相关的啊!

在这里插入图片描述

这个问题咱也遇到了很多次了,无论这个t的类型是左值引用还是右值引用,左值引用(int& t)它的属性是左值,右值引用(int&& t)它的属性会退化成左值属性,因此都全部走左值引用的Fun函数了。那如果用move(t)强转呢?首先这个t的类型是编译器根据传入的值去引用折叠推导,咱也不知道那个是左值就给它强转成右值啊,你直接move那就全部走右值引用的Fun函数了所以强转也无法解决问题,因此就用到完美转发 forward<T>(t)

你这个t的类型是int&&,t的属性退化成左值,那就给你强转成右值,t的类型是int&,那就给你强转成左值。它的底层本质上还是强转,只不过它会去判断,无需咱们操心

Fun(forward<T>(t));
http://www.dtcms.com/wzjs/508209.html

相关文章:

  • 下载软件的软件哪个好站长seo
  • 景安 怎么把网站做别名合肥网站排名
  • 网站的ppt方案怎么做网络seo首页
  • 不连接wordpress安装优化关键词排名的工具
  • 2023年税收优惠政策淘宝seo优化排名
  • 网站被攻击空间关了怎么办武汉seo公司哪家专业
  • p2p网贷网站建设哪家好成都最新数据消息
  • 怎么做网站里插入背景音乐企业网站seo推广
  • 台州微网站建设网络游戏推广
  • 如何创建个人博客网站网站优化推广培训
  • 做网站公司哪家靠谱中国公关公司前十名
  • 学校英语网站栏目名称梅州网络推广
  • 广州白云机场网站建设服装营销方式和手段
  • wordpress添加锚湛江seo
  • 淘宝客api同步到网站360推广登录入口官网
  • 做网站h5陕西今日头条新闻
  • 泉州微信网站建设百度导航最新版本免费下载
  • 厦门公司注册代理网站优化推广哪家好
  • 昆明做商城网站多少钱营销网络怎么写
  • 做网站开发学什么网络优化报告
  • 成都的教育品牌网站建设下载百度到桌面上
  • 潍坊网站制作培训小红书信息流广告
  • 建立一个公司网站大约多少钱磁力搜索引擎
  • 郑州全网营销推广自己怎么优化网站
  • 优惠券的网站怎么做的营销平台有哪些
  • 网站建设行业分析seo优化查询
  • abc网站建设是什么意思百度认证中心
  • 小程序怎么推广引流北京推广优化公司
  • 局 网站建设方案大连seo顾问
  • 用wordpress做网站3小时百度收录新站方法