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

【C++】C++11介绍列表初始化右值引用和移动语义

个人主页 : zxctscl
如有转载请先通知

文章目录

  • 1. C++11简介
  • 2. 统一的列表初始化
    • 2.1{}初始化
    • 2.2 std::initializer_list
  • 3. 声明
    • 3.1 auto
    • 3.2 decltype
    • 3.3 nullptr
  • 4. 范围for循环
    • 4.1 范围for的语法
    • 4.2 范围for的使用条件
  • 5. STL中一些变化
  • 6. 右值引用和移动语义
    • 6.1 左值引用和右值引用
      • 6.1.1 左值引用
        • 6.1.1.1 左值
        • 6.1.1.2 左值引用
      • 6.1.2 右值引用
        • 6.1.2.1 右值
        • 6.1.2.2 右值引用
    • 6.2 右值引用意义
    • 6.3 左值引用与右值引用比较
    • 6.4 右值引用引用左值及其一些更深入的使用场景分析
    • 6.5 完美转发

1. C++11简介

在这里插入图片描述

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。
从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比于C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以要作为一个重点去学习。C++11增加的语法特性非常篇幅非常多,我们这里没办法一 一讲解,所以主要讲解实际中比较实用的语法。

C++11链接: https://en.cppreference.com/w/cpp/11

小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫C++ 07。但是到06年的时候,官方觉得2007年肯定完不成C++ 07,而且官方觉得2008年可能也完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11。

2. 统一的列表初始化

2.1{}初始化

在C语言中结构体和数组的初始化,而C++兼容长。在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
比如:

struct Point
{int _x;int _y;
};int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围:

自定义类型不能像这样初始化:

class A
{
private:int _x;int _y;
};
int main()
{A aa1 = { 2,2 };return 0;
}

在这里插入图片描述
但想要也可以,利用隐式类型转换:

class A
{
public:A(int x, int y):_x(x),_y(y){}
private:int _x;int _y;
};int main()
{//多参数的隐式类型转换,本质就是用{2,2}去构造一个A对象,A对象再去拷贝构造。而编译器就直接优化直接构造A aa1 = { 2,2 };return 0;
}

这个就是参考单参数的构造函数的隐式类型转换:
都是构造+拷贝构造直接优化为构造

class A
{
public:A(int x, int y):_x(x), _y(y){}A(int x):_x(x), _y(x){}
private:int _x;int _y;
};
int main()
{// 单参数的隐式类型转换A aa2 = 1;// 多参数的隐式类型转换A aa1 = { 2,2 };//不加const,就不行。因为类型转化,中间会生成一个临时对象,而临时对象具有常性,就得加constconst A& aa3 = { 2,2 };return 0;
}

也可以加explicit,此时就不会发生隐式类型转换:
在这里插入图片描述
而C++11对单参数也可以一切皆可列表初始化:

class A
{
public://explicit A(int x, int y)A(int x, int y):_x(x), _y(y){}A(int x):_x(x), _y(x){}
private:int _x;int _y;
};int main()
{// 单参数的隐式类型转换A aa2 = 1;A aa4 = { 1 };return 0;
}

在这里插入图片描述

使其可用于所有的内置类型和用户自定义的类型,使用列表初始化时,可添加等号(=),也可不添加。

class A
{
public://explicit A(int x, int y)A(int x, int y):_x(x), _y(y){}A(int x):_x(x), _y(x){}
private:int _x;int _y;
};int main()
{int x1 = 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// 单参数的隐式类型转换A aa2 = 1;A aa4 = { 1 };A aa5 { 1 };// 多参数的隐式类型转换A aa1 = { 2,2 };A aa6 { 2,2 };return 0;
}

内置类型也能用:
在这里插入图片描述

在这里插入图片描述
创建对象时也可以使用列表初始化方式调用构造函数初始化

class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(2022, 1, 1); // old style// C++11支持的列表初始化,这里会调用构造函数初始化Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };return 0;
}

一切皆可用列表初始化,可以省略=

2.2 std::initializer_list

为了支持容器的列表初始化,就有了initializer_list
在这里插入图片描述

std::initializer_list的介绍文档: https://cplusplus.com/reference/initializer_list/initializer_list/

int main()
{vector<int> v1;vector<int> v2(10, 1);auto il1 = { 10, 20, 30 };initializer_list<int> il2 = { 10, 20, 30 };cout << typeid(il1).name() << endl;return 0;
}

std::initializer_list是什么类型:

int main()
{auto il1 = { 10, 20, 30 };initializer_list<int> il2 = { 10, 20, 30 };cout << typeid(il1).name() << endl;return 0;
}

在这里插入图片描述
它有一个first和last
在这里插入图片描述
first指向第一个位置,也就是10,last指向最后一个位置的下一个:
在这里插入图片描述
它底层就是两个指针,一个指向开始位置,一个指向结束的位置:
在这里插入图片描述

所以initializer_list支持迭代器,迭代器返回的就是const T*
在这里插入图片描述

所以遍历initializer_list就可以用范围for遍历。

容器想要不固定的{ }数据个数初始化:

vector(const T& x1)
vector(const T& x1, const T& x2)
vector(const T& x1, const T& x2, const T& x3)
vector(const T& x1, const T& x2, const T& x3, const T& x4)
// ...

他就这个构造一劳永逸的解决了问题,不用像上面那样麻烦的方式解决

 vector(initializer_list<T> il);
	// 构造vector<int> v5({ 1,2,3,4,5 });//隐式类型转换// X自定义 = Y类型 ->隐式类型转换 X(Y mm)  X支持Y为参数类型构造就可以vector<int> v3 = {1,2,3,4,5};vector<int> v4{ 10,20,30};

容器想要不固定的{ }数据个数初始化,initializer_list支持

在这里插入图片描述

所有容器都支持initializer_list,像map:
在这里插入图片描述

int main()
{pair<string, string> kv1("sort", "排序");pair<string, string> kv2("insert", "插入");map<string, string> dict1 = {kv1, kv2};// 1、pair多参数隐式类型转换// 2、initializer_list<pair>的构造map<string, string> dict2 = { {"sort", "排序"}, {"insert", "插入"} };return 0;
}

在这里插入图片描述

3. 声明

c++11提供了多种简化声明的方式,尤其是在使用模板时。

3.1 auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。

int main()
{int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin();return 0;
}

这里x加加,不影响i:

int main()
{int i = 0;auto x = i;x++;return 0;
}

在这里插入图片描述
但是如果用引用,此时x就是i的别名,那么值就都加加
在这里插入图片描述

int main()
{int i = 0;auto x = i;x++;int& j = i;auto y = j;return 0;
}

这里y不是引用,他们地址不一样,j是i的引用,y是一个拷贝:
在这里插入图片描述

y想要变成引用,还得加&
在这里插入图片描述

3.2 decltype

关键字decltype将变量的类型声明为表达式指定的类型。

int main()
{list<int>::iterator it1;// typeid推出时一个单纯的字符串cout << typeid(it1).name() << endl;return 0;
}

在这里插入图片描述

typeid不能用来定义对象在这里插入图片描述

decltype可以用来定义对象:

int main()
{list<int>::iterator it1;// typeid推出时一个单纯的字符串cout << typeid(it1).name() << endl;// 可以用来定义对象decltype(it1) it2;cout << typeid(it2).name() << endl;return 0;
}

auto也可以自动推导:

	list<int>::iterator it1;// typeid推出时一个单纯的字符串cout << typeid(it1).name() << endl;// 可以用来定义对象decltype(it1) it2;cout << typeid(it2).name() << endl;auto it3 = it1;cout << typeid(it3).name() << endl;

在这里插入图片描述

但是下面这样呢?

template<class T>
class B
{
public:T* New(int n){return new T[n];}
};auto func1()
{list<int> lt;auto ret = lt.begin();return ret;
}int main()
{auto ret3 = func1();B<decltype(ret3)> bb1;return 0;
}

decltype推导出来的类型,不仅仅可以帮助定义对象,也可以做模版的传参。

template<class T>
class B
{
public:T* New(int n){return new T[n];}
};auto func1()
{list<int> lt;auto ret = lt.begin();return ret;
}int main()
{auto ret3 = func1();B<decltype(ret3)> bb1;map<string, string> dict2 = { {"sort", "排序"}, {"insert", "插入"} };auto it4 = dict2.begin();B<decltype(it4)> bb2;B<std::map<std::string, std::string>::iterator> bb2;// auto和decltype有些地方增加代码读起来难度return 0;
}

3.3 nullptr

由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

4. 范围for循环

4.1 范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{int array[] = { 1, 2, 3, 4, 5 };for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)array[i] *= 2;for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)cout << *p << endl;
}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。


void TestFor()
{int array[] = { 1, 2, 3, 4, 5 };for (auto& e : array)e *= 2;for (auto e : array)cout << e << " ";return 0;
}

注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

4.2 范围for的使用条件

  1. for循环迭代的范围必须是确定的
    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。
    注意:以下代码就有问题,因为for的范围不确定
void TestFor(int array[])
{for (auto& e : array)cout << e << endl;
}
  1. 迭代的对象要实现++和==的操作。

5. STL中一些变化

新容器
用橘色圈起来是C++11中的一些几个新容器,但是实际最有用的是unordered_map和unordered_set。这两个前面已经进行了分享,有需要点这个链接: 【C++】unordered系列关联式容器及其底层结构 其他了解一下即可。

在这里插入图片描述
array相比于静态数组,检查越界更好。
但不如vector好用:

int main()
{array<int, 10> a1;vector<int> a2(10, 0);return 0;
}

在这里插入图片描述
forward_list最大的优点就是只头插、头删的时候用:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

容器中的一些新方法:
如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。
比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作.
实际上C++11更新后,容器中增加的新方法最后用的插入接口函数的右值引用版本:

https://cplusplus.com/reference/vector/vector/emplace_back/
https://cplusplus.com/reference/vector/vector/push_back/
https://cplusplus.com/reference/map/map/insert/
https://cplusplus.com/reference/map/map/emplace/

6. 右值引用和移动语义

6.1 左值引用和右值引用

6.1.1 左值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名

6.1.1.1 左值

简单而言就是:
左值:可以取地址;
右值:不可以取地址。

此时a,b,c都是左值:

int main()
{// 左值和右值,能否取地址// 左值:可以取地址的// 右值:不可以取地址的int a = 10;int b = a;const int c = 10;cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}

在这里插入图片描述

左值不一定是个值,也可以是表达式:

int main()
{// 左值是一个表达式,可以取地址的int* p = &a;cout << &(*p) << endl;return 0;
}

在这里插入图片描述

什么是左值?什么是左值引用?

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

int main()
{// 左值是一个表达式,可以取地址的vector<int> v(10, 1);v[1];cout << &(v[1]) << endl;return 0;
}

在这里插入图片描述

int main()
{
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;return 0;
}
6.1.1.2 左值引用

左值引用:

// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

6.1.2 右值引用

什么是右值?什么是右值引用?
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。

6.1.2.1 右值

常量、匿名对象、临时对象这些都是右值:

int main()
{// 10、string("1111")、to_string(123), x+ycout << &10 << endl;cout << &string("1111") << endl;cout << &to_string(123) << endl;return 0;
}

在这里插入图片描述

像下面这样也是右值:

    int x = 1, y = 2;cout << &(x + y) < endl;
int main()
{double x = 1.1, y = 2.2;// 以下几个都是常见的右值10;x + y;fmin(x, y);// 这里编译会报错:error C2106: “=”: 左操作数必须为左值10 = 1;x + y = 1;fmin(x, y) = 1;// 以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);return 0;
}

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇,这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。

6.1.2.2 右值引用

右值引用:

	// 以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);

那么左值引用能否给右值引用起别名?
不可以,但是const左值引用可以。

	const string& ref1 = string("1111");const int& ref2 = 10;

右值引用能否给左值取别名?
不可以,但是可以给move以后的左值区别

int main()
{// 右值引用能否给左值取别名 -- 不可以,但是可以给move以后的左值区别string s1("1111");string&& rref5 = move(s1);return 0;
}

在这里插入图片描述

6.2 右值引用意义

前面我们可以看到左值引用既可以引用左值和又可以引用右值,那为什么C++11还要提出右值引用呢?是不是化蛇添足呢?

首先得知道引用的意义是减少拷贝,提高效率

左值引用的场景:

void func1(const string& s);
string& func2();//传引用返回,减少拷贝,提高效率

左值引用返回值的问题没有彻底解决,如果返回值是func2中局部对象,不能用引用返回。

来看看下面这段代码:

namespace bit
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str) -- 构造" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造// 左值string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}// 移动构造// 右值(将亡值)string(string&& s):_str(nullptr){cout << "string(string&& s) -- 移动构造" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};bit::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}bit::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}int main()
{bit::string ret1 = bit::to_string(1234);return 0;
}

这里是构造加构造,两次拷贝构造
在这里插入图片描述

但是编译器在这里做了个优化:合二为一,直接构造:

在这里插入图片描述

将ret1这样写,这里就完全没有优化了:先构造,构造完了还要拷贝构造,就有两次构造任何传值返回。这样拷贝构造生成一个对象,拷贝构造对象也没有部分合二为一,因为下一个是赋值。

	bit::string ret1;ret1 = bit::to_string(1234);

在这里插入图片描述

要返回str的别名,但是str已经销毁了:
在这里插入图片描述

在这里插入图片描述

不用引用返回:
这里的栈帧图:在main里有个ret1对象,to_string里面有一个str,str指向一个空间,如果编译器不优化,中间还会产生一个临时变量,会先拷贝这个临时对象,销毁str指向的空间,再用这个临时对象,再去拷贝构造ret1的。编译器优化之后,在str返回之前,就构造一个给ret1,再销毁str的,此时str也跟着一起销毁。

在这里插入图片描述

用右值引用,还是返回它的别名,没有拷贝。打破中间的优化,没有办法将两次构造合二为一。
左值引用和右值引用结果都一样,str这个对象已经销毁了。
在这里插入图片描述

右值引用和移动语义解决上述问题:
在bit::string中增加移动构造,移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

再运行上面bit::to_string的两个调用,我们会发现,这里没有调用深拷贝的拷贝构造,而是调用了移动构造,移动构造中没有新开空间,拷贝数据,所以效率提高了
在这里插入图片描述

不仅仅有移动构造,还有移动赋值:
在bit::string类中增加移动赋值函数,再去调用bit::to_string(1234),不过这次是将bit::to_string(1234)返回的右值对象赋值给ret1对象,这时调用的是移动构造。
在这里插入图片描述

这里运行后,我们看到调用了一次移动构造和一次移动赋值。因为如果是用一个已经存在的对象接收,编译器就没办法优化了。bit::to_string函数中会先用str生成构造生成一个临时对象,但是我们可以看到,编译器很聪明的在这里把str识别成了右值,调用了移动构造。然后在把这个临时对象做为bit::to_string函数调用的返回值赋值给ret1,这里调用的移动赋值。
在这里插入图片描述
在这里插入图片描述

STL中的容器都是增加了移动构造和移动赋值: https://cplusplus.com/reference/string/string/string/
https://cplusplus.com/reference/vector/vector/vector/

6.3 左值引用与右值引用比较

左值引用总结:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值。
int main()
{// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a; // ra为a的别名//int& ra2 = 10; // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;
}

右值引用总结:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值
int main()
{// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return 0;
}

6.4 右值引用引用左值及其一些更深入的使用场景分析

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能真的需要用右值去引用左值实现移动语义。当需要用右值引用引用一个左值时,可以通过move函数将左值转化为右值。C++11中,std::move()函数位于 头文件中,该函数名字具有迷惑性,它并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义

template<class _Ty>
inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
{// forward _Arg as movablereturn ((typename remove_reference<_Ty>::type&&)_Arg);
}
int main()
{bit::string s1("hello world");// 这里s1是左值,调用的是拷贝构造bit::string s2(s1);// 这里我们把s1 move处理以后, 会被当成右值,调用移动构造// 但是这里要注意,一般是不要这样用的,因为我们会发现s1的// 资源被转移给了s3,s1被置空了。bit::string s3(std::move(s1));return 0;
}

https://cplusplus.com/reference/list/list/push_back/
http://www.cplusplus.com/reference/vector/vector/push_back/

在这里插入图片描述

void push_back (value_type&& val);
int main()
{list<bit::string> lt;bit::string s1("1111");// 这里调用的是拷贝构造lt.push_back(s1);// 下面调用都是移动构造lt.push_back("2222");lt.push_back(std::move(s1));return 0;
}

s1是一个左值,就老实拷贝。如果是右值,就转移资源:
在这里插入图片描述
上面效率提升,针对的是自定义类型的深拷贝的类,因为深拷贝的类才有转移资源的移动系列函数
对于内置类型,和浅拷贝自定义类型,没有移动系列函数

6.5 完美转发

模板中的&& 万能引用

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<typename T>
void PerfectForward(T&& t)
{Fun(t);
}int main()
{PerfectForward(10);           // 右值int a;PerfectForward(a);            // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);		      // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

std::forward 完美转发在传参的过程中保留对象原生类型属性

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; }
// std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
template<typename T>
void PerfectForward(T&& t)
{Fun(std::forward<T>(t));
}
int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0;
}

完美转发实际中的使用场景:

template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};
template<class T>
class List
{typedef ListNode<T> Node;
public:List(){_head = new Node;_head->_next = _head;_head->_prev = _head;}void PushBack(T&& x){//Insert(_head, x);Insert(_head, std::forward<T>(x));}void PushFront(T&& x){//Insert(_head->_next, x);Insert(_head->_next, std::forward<T>(x));}void Insert(Node* pos, T&& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = std::forward<T>(x); // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}void Insert(Node* pos, const T& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = x; // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}
private:Node* _head;
};
int main()
{List<bit::string> lt;lt.PushBack("1111");lt.PushFront("2222");return 0;
}

有问题请指出,大家一起进步!!!

相关文章:

【C++】C++11介绍列表初始化右值引用和移动语义

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. C11简介2. 统一的列表初始化2.1&#xff5b;&#xff5d;初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype3.3 nullptr 4. 范围for循环4.1 范围for的语法4.2 范围for的使用条件 5. STL中一些变化6. 右…...

基于Spring Boot的高校普法系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

算法为舟 思想为楫:AI时代,创作何为?

在科技浪潮汹涌澎湃的当下,AI技术以前所未有的态势席卷各个领域,创作领域亦未能幸免。当生成式AI展现出在剧本撰写、诗歌创作、图像设计等方面的惊人能力时,人类创作者仿佛置身于文明演化的十字路口,迷茫与困惑交织,兴奋与担忧并存。在AI时代,创作究竟该何去何从?这不仅…...

GPT-4o 原生图像生成技术解析:从模型架构到吉卜力梦境的实现

最近不少 AI 爱好者、设计师、Vlogger 在社交平台晒出了 GPT-4o 生成的梦幻图像&#xff0c;尤其是吉卜力风格的作品——柔和光影、日系构图、治愈色彩、富有情感的角色表达&#xff0c;一下子击中了无数人的“童年回忆 审美舒适区”。 &#x1f3a8; 下面是一些 GPT-4o 实际生…...

无线通信技术(二):ITU、3GPP及传统波段对无线频谱的划分

本文介绍国际标准组织ITU、3GPP和传统波段对无线频谱的划分 。 一.ITU波段划分 国际电信联盟&#xff08;ITU&#xff09;将无线电频谱划分为多个频段&#xff0c;并根据频率范围和业务需求分配用途。 ITU对无线频谱的划分 带号频带名称频率范围波长范围波段名称典型应用…...

[Python学习日记-88] 并发编程之多进程 —— 队列与生产者消费者模型

[Python学习日记-88] 并发编程之多进程 —— 队列与生产者消费者模型 简介 队列 一、队列的介绍 二、队列的使用 生产者消费者模型 一、为什么要使用生产者消费者模型 二、什么是生产者消费者模型 三、生产者消费者模型的优势 四、生产者消费者模型的实现 JoinableQ…...

Vue学习笔记集--computed

computed 在 Vue 3 的 Composition API 中&#xff0c;computed 用于定义响应式计算属性 它的核心特性是自动追踪依赖、缓存计算结果&#xff08;依赖未变化时不会重新计算&#xff09; 基本用法 1. 定义只读计算属性 import { ref, computed } from vue;const count ref(…...

python之多线程,多进程理解

目录 一,什么是多线程多进程 1,1 多线程 1.2 多进程 二,多线程 2.1 使用threading模块 三,多进程 3.1 使用multiprocessing模块 3.2 多进程的优势 3.3 进程间的通信 四,如何选择多进程还是多线程 五,异步编程的替代方案(协程) 在开发过程中&#xff0c;提升程序的并…...

3月29日星期六今日早报简报微语报早读

3月29日星期六&#xff0c;农历三月初一&#xff0c;早报#微语早读。 1、全国公立医疗机构自3月31日起全面停止收取门诊预交金&#xff1b; 2、永辉超市“胖东来调改店”已达47家店 一线员工薪酬涨幅50%以上&#xff1b; 3、两孩家庭补10万&#xff0c;三孩家庭补20万&#…...

栈:隐匿于计算机科学长卷的璀璨明珠

目录 &#x1f680;前言&#x1f31f;栈的概念&#x1f914;栈的两种实现形式&#x1f4af;数组栈实现&#x1f4af;链表栈实现 ⚙️数组栈与链表栈对比&#x1f427;递归与栈&#x1f4bb;总结 &#x1f680;前言 大家好&#xff01;我是 EnigmaCoder。 在计算机科学的宏大版图…...

【万字总结】前端全方位性能优化指南(七)——按需加载、虚拟列表、状态管理

现代框架高阶优化——突破复杂场景的性能临界点 当Web应用进入「十万级组件、百万级数据」的复杂场景时,传统优化手段开始触及框架底层瓶颈:Redux的单一Store引发级联渲染风暴、全量加载的首屏资源阻塞关键交互、长列表滚动导致内存飙升直至页面崩溃……这些痛点正在倒逼框架…...

合并石子 | 第十四届蓝桥杯省赛JavaB组

在桌面从左至右横向摆放着 N 堆石子。 每一堆石子都有着相同的颜色&#xff0c;颜色可能是颜色 0&#xff0c;颜色 1 或者颜色 2 中的其中一种。 现在要对石子进行合并&#xff0c;规定每次只能选择位置相邻并且颜色相同的两堆石子进行合并。 合并后新堆的相对位置保持不变&…...

【商城实战(94)】构建高并发的负载均衡与集群架构

【商城实战】专栏重磅来袭&#xff01;这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建&#xff0c;运用 uniapp、Element Plus、SpringBoot 搭建商城框架&#xff0c;到用户、商品、订单等核心模块开发&#xff0c;再到性能优化、安全加固、多端适配&#xf…...

鸿蒙开发:了解Canvas绘制

前言 本文基于Api13 系统的组件无法满足我们的需求&#xff0c;这种情况下就不得不自己自定义组件&#xff0c;除了自定义组合组件&#xff0c;拓展组件&#xff0c;还有一种方式&#xff0c;那就是完全的自绘制组件&#xff0c;这种情况&#xff0c;常见的场景有&#xff0c;比…...

Ubuntu和Windows实现文件互传

1.开启Ubuntu下的FTP服务&#xff1a; &#xff08;1&#xff09;终端输入&#xff1a; sudo apt-get install vsftpd&#xff08;2&#xff09;安装完成后&#xff1a; 终端输入&#xff1a; /etc 是 Linux 系统的全局配置文件目录&#xff0c;存储系统和应用程序的配置信息…...

dav_pg8_vacuum

一、VACUUM基础概念 1.1 VACUUM的作用 在PostgreSQL中&#xff0c;当数据被更新或删除时&#xff0c;系统并不会立即释放物理空间&#xff0c;而是将其标记为 “可重用”。 随着时间推移&#xff0c;表中的死元组&#xff08;已删除或已被新版本覆盖的数据&#xff09;会越来越…...

革新汽车安全通信技术,美格智能全系车载通信模组支持NG-eCall

根据QYR&#xff08;恒州博智&#xff09;的统计及预测&#xff0c;2024年全球汽车无线紧急呼叫&#xff08;eCall&#xff09;设备市场销售额达到了25.17亿美元&#xff0c;预计2031年将达到44.97亿美元&#xff0c;年复合增长率&#xff08;CAGR 2025-2031&#xff09;为8.8%…...

Ubuntu桌面环境下网络设置选项缺失问题解决

一、问题现象 在Ubuntu桌面环境中&#xff0c;网络设置界面中仅显示VPN设置&#xff0c;未显示常规网络配置选项&#xff0c;导致无法通过图形界面修改网络配置。但通过命令行工具可正常设置网络。 二、解决方案 &#xff08;一&#xff09;检查网络设备状态 nmcli d 发现…...

GitHub绑定本地计算机以及仓库创建跟推送指南

GitHub绑定到本地计算机 要在本地计算机上连接到你的GitHub账户&#xff0c;可以通过以下步骤实现&#xff1a; 1. 检查和安装Git 确保你的计算机上已经安装了Git。如果还没有安装&#xff0c;可以从Git官网下载并安装。 2. 配置Git 打开终端&#xff08;macOS或Linux&…...

【数据结构】导航

【数据结构】-CSDN博客 【数据结构】next数组、nextval数组-CSDN博客...

Java内存中的Heap(堆)的作用

Java内存中的Heap&#xff08;堆&#xff09;的作用 在 Java 的内存模型中&#xff0c;Heap&#xff08;堆&#xff09; 是 JVM&#xff08;Java Virtual Machine&#xff09;管理的运行时数据区域之一&#xff0c;主要用于存储程序运行过程中动态分配的对象和数据。它是 Java…...

Python控制结构详解

前言 一、控制结构概述 二、顺序结构 三、选择结构&#xff08;分支结构&#xff09; 1. 单分支 if 2. 双分支 if-else 3. 多分支 if-elif-else 4.实际应用: 四、循环结构 1. for循环 2. while循环 3. 循环控制语句 五、异常处理&#xff08;try-except&#xff09…...

2023第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组(真题题解)(C++/Java题解)

本来想刷省赛题呢&#xff0c;结果一不小心刷成国赛了 真是个小迷糊〒▽〒 但&#xff0c;又如何( •̀ ω •́ )✧ 记录刷题的过程、感悟、题解。 希望能帮到&#xff0c;那些与我一同前行的&#xff0c;来自远方的朋友&#x1f609; 大纲&#xff1a; 一、子2023-&#xff…...

JVM介绍

JVM类加载器 栈指令重排序 类的JVM内存分配 堆内存GC模型...

HTML输出流

HTML 输出流 JavaScript 中**「直接写入 HTML 输出流」**的核心是通过 document.write() 方法向浏览器渲染过程中的数据流动态插入内容。以下是详细解释&#xff1a; 一、HTML 输出流的概念 1. 动态渲染过程 HTML 文档的加载是自上而下逐行解析的。当浏览器遇到 <script&…...

Kafka 的高可用性

Kafka 的高可用性主要通过副本机制、ISR&#xff08;In-Sync Replicas&#xff09;列表和控制器 Broker 来实现。这些机制共同确保了 Kafka 集群在部分节点故障时仍然可以正常运行&#xff0c;数据不会丢失&#xff0c;并且服务不会中断。 1. 副本机制 Kafka 的副本机制是其高…...

Centos7,tar包方式部署rabbitmq-3.7.6

1. 环境准备 安装编译工具和依赖包 yum -y install make gcc gcc-c glibc-devel m4 perl openssl openssl-devel ncurses-devel ncurses-devel xz xmlto perl 2. Erlang环境搭建 版本对应&#xff1a;https://www.rabbitmq.com/docs/which-erlang 解压到指定目录 tar -xv…...

RISC-V AIA学习3---APLIC 第二部分(APLIC 中断域的内存映射控制区域)

每个中断域都有一个专用的内存映射控制区域&#xff0c;用来处理该中断域的中断。 控制区域的大小是 4KB 的倍数&#xff0c;对齐到 4KB 边界。最小的有效控制区域是 16KB。 1. 控制区域的基本结构&#xff1a;部门文件柜 每个中断域就像公司的一个部门&#xff0c;有自己的 …...

顶刊【遥感目标检测】【TGRS】FFCA-YOLO遥感图像小目标检测

FFCA-YOLO for Small Object Detection in Remote Sensing Images FFCA-YOLO遥感图像小目标检测 0.论文摘要 摘要——特征表征不足、背景干扰等问题使得遥感图像中的小目标检测任务极具挑战性。尤其在算法需部署于星载设备进行实时处理时&#xff0c;需在有限计算资源下对精度…...

【人工智能】从 Llama 到 DeepSeek:开源大模型的演进与技术对比

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着人工智能的迅猛发展,开源大语言模型(LLM)在自然语言处理领域扮演着越来越重要的角色。本文从 Meta 的 Llama 系列开始,追溯开源大模…...

【PCB工艺】时序图(Timing Diagram)

时序图&#xff08;Timing Diagram&#xff09;是描述数字电路信号随时间变化的图示&#xff0c;广泛用于分析和设计时序逻辑电路&#xff0c;如锁存器&#xff08;Latch&#xff09;、触发器&#xff08;Flip-Flop&#xff09;、计数器、状态机等。这篇文章从时序图的原理、构…...

MATLAB 中,并行池(Parallel Pool)自动关闭的情况

在 MATLAB 中&#xff0c;并行池&#xff08;Parallel Pool&#xff09;的行为可以通过设置进行控制&#xff0c;但默认情况下&#xff0c;并行池不会自动关闭&#xff0c;除非满足某些条件或显式调用关闭命令。以下是关于并行池自动关闭机制的详细说明&#xff1a; 自动关闭的…...

[网安工具] SQL 注入自动探测工具 —— SQLMAP 使用手册

&#x1f31f;想了解其它网安工具&#xff1f;看看这个&#xff1a;[网安工具] 网安工具库 —— 工具管理手册 https://github.com/sqlmapproject/sqlmaphttps://github.com/sqlmapproject/sqlmap用法 | sqlmap 用户手册https://sqlmap.highlight.ink/usage 0x01&#xff1a;S…...

Python数据结构与算法-基础预热篇

目录 语言基础 1.内置函数 1.1math库 1.2collections 1.2.1Counter&#xff1a;计数器 1.2.2deque双端对列 1.2.3defaultdict有默认值的字典 1.3heapq堆&#xff08;完全二叉树&#xff09; 1.4functool 1.5itertools 1.5.1无限迭代器 1.5.2有限迭代器 1.5.3排列组合迭代器 2.序…...

构建可扩展、可靠的网络抓取、监控和自动化应用程序的终极指南

大家好&#xff0c;这里是架构资源栈&#xff01;点击上方关注&#xff0c;添加“星标”&#xff0c;一起学习大厂前沿架构&#xff01; 无论您是企业主、营销人员还是软件开发人员&#xff0c;您都很有可能在某个时候使用过 Web 自动化工具。每个人都希望更聪明地工作&#xf…...

【蓝桥杯】重点冲刺

【最高优先级】必考核心算法(占分60%以上) 动态规划(DP) 🌟🌟🌟 背包问题:01背包、完全背包(必须掌握空间优化的一维写法) 线性DP:最长上升子序列(LIS)、最长公共子序列(LCS) 路径问题:网格路径计数(含障碍物)、最小路径和 经典模型:打家劫舍、股票买卖问…...

质量工程师的2025:从“找bug“到“造质量“的职业进化

想象一下&#xff0c;2025年的某天&#xff1a;阅读原文 早晨&#xff0c;AI测试助手已经自动运行了夜间回归测试&#xff0c;并将可疑问题标记出来 你喝着咖啡&#xff0c;通过质量数据看板分析系统健康度 下午的会议上&#xff0c;你正用业务语言向产品经理解释&#xff1a…...

2025年CNG 汽车加气站操作工题目分享

CNG 汽车加气站操作工题目分享&#xff1a; 单选题 1、CNG 加气站中&#xff0c;加气机的加气软管应&#xff08; &#xff09;进行检查。 A. 每天 B. 每周 C. 每月 D. 每季度 答案&#xff1a;A 解析&#xff1a;加气软管是加气操作中频繁使用的部件&#xff0c;每天检…...

【QT5 多线程示例】线程池

线程池 【C并发编程】&#xff08;九&#xff09;线程池 QThreadPool 和 QRunnable 是 Qt 提供的线程池管理机制。QRunnable 是一个任务抽象类&#xff1b;定义任务逻辑需要继承QRunnable 并实现 run() 方法。QThreadPool 负责管理线程&#xff0c;并将 QRunnable 任务分配到…...

飞致云荣获“Alibaba Cloud Linux最佳AI镜像服务商”称号

2025年3月24日&#xff0c;阿里云云市场联合龙蜥社区发布“2024年度Alibaba Cloud Linux最佳AI镜像服务商”评选结果。 经过主办方的严格考量&#xff0c;飞致云&#xff08;即杭州飞致云信息科技有限公司&#xff09;凭借旗下MaxKB开源知识库问答系统、1Panel开源面板、Halo开…...

FAST-LIVO2 Fast, Direct LiDAR-Inertial-Visual Odometry论文阅读

FAST-LIVO2 Fast, Direct LiDAR-Inertial-Visual Odometry论文阅读 论文下载论文翻译FAST-LIVO2: 快速、直接的LiDAR-惯性-视觉里程计摘要I 引言II 相关工作_直接方法__LiDAR-视觉&#xff08;-惯性&#xff09;SLAM_ III 系统概述IV 具有顺序状态更新的误差状态迭代卡尔曼滤波…...

kubesphere 终端shell连不上的问题

使用nginx代理kubesphere控制台会出现容器的终端shell连不上的问题 下面是一个样例配置可以解决这个问题&#xff1a; 注意修改为你的ip地址&#xff1a; upstream k8s { ip_hash; server masterip1:30880; server masterip2:30880; server masterip3:30880; } nginx.conf #…...

无人机,雷达定点飞行时,位置发散,位置很飘,原因分析

参考&#xff1a; 无人车传感器 IMU与GPS数据融合进行定位机制_gps imu 组合定位原始数-CSDN博客 我的无人机使用雷达定位&#xff0c;位置模式很飘 雷达的更新频率也是10HZ&#xff0c; 而px飞控的频率是100HZ&#xff0c;没有对两者之间的频率差异做出处理 所以才导致无人…...

外星人入侵(python设计小游戏)

这个游戏简而言之就是操作一个飞机对前方的飞船进行射击&#xff0c;和一款很久之前的游戏很像&#xff0c;这里是超级低配版那个游戏&#xff0c;先来看看效果图&#xff1a; 由于设计的是全屏的&#xff0c;所以电脑不能截图。。。。 下面的就是你操控的飞船&#xff0c;上面…...

Stereolabs ZED Box Mini:NVIDIA Orin™驱动,双GMSL2输入,智能机器视觉AI新选择”

Stereolabs近日推出了ZED Box Mini&#xff0c;这是一款专为视觉AI设计的紧凑型迷你电脑&#xff08;ECU&#xff09;。该产品搭载了NVIDIA Orin™系列处理器&#xff0c;具备强大的AI视觉处理能力&#xff0c;适用于机器人、智能基础设施和工业应用等多种场景。ZED Box Mini以…...

IP协议的介绍

网络层的主要功能是在复杂的网络环境中确定一个合适的路径.网络层的协议主要是IP协议.IP协议头格式如下: 1.4位版本号:指定IP协议的版本,常用的是IPV4,对于IPV4来说,这里的值就是4. 2.4位头部长度,单位也是4个字节,4bit表示的最大数字是15,因此IP头部的最大长度就是60字节 3.…...

【入门初级篇】布局类组件的使用(2)

【入门初级篇】布局类组件的使用&#xff08;2&#xff09; 视频要点 &#xff08;1&#xff09;2分栏场景介绍与实操演示 &#xff08;2&#xff09;3分栏场景介绍与实操演示 点击访问myBuilder产品运营平台 CSDN站内资源下载myBuilder 交流请加微信&#xff1a;MyBuilder8…...

高并发金融系统,“可观测-可追溯-可回滚“的闭环审计体系

一句话总结 在高并发金融系统中&#xff0c;审计方案设计需平衡"观测粒度"与"系统损耗"&#xff0c;通过双AOP实现非侵入式采集&#xff0c;三表机制保障操作原子性&#xff0c;最终形成"可观测-可追溯-可回滚"的闭环体系。 业务痛点与需求 在…...

(九)Spring Webflux

底层基于Netty实现的Web容器与请求/响应处理机制 参照&#xff1a;Spring WebFlux :: Spring Frameworkhttps://docs.spring.io/spring-framework/reference/6.0/web/webflux.html 一、组件对比 API功能 Servlet-阻塞式Web WebFlux-响应式Web 前端控制器 DispatcherServl…...

如何在Webpack中配置别名路径?

如何在Webpack中配置别名路径&#xff1f; 文章目录 如何在Webpack中配置别名路径&#xff1f;1. 引言2. 配置别名路径的基本原理3. 如何配置别名路径3.1 基本配置3.2 结合Babel与TypeScript3.2.1 Babel配置3.2.2 TypeScript配置 3.3 适用场景与最佳实践 4. 调试与常见问题4.1 …...