可变参数模板、右值引用带来的移动语义完美转发、lambda表达式的理解

可变参数模板

可变参数模板对参数进行了高度泛化,可以表示任意数目、任意类型的参数:
语法为:在class或者typename后面带上省略号。

Template<class ... T>
void func(T ... args)
{
    
    
	//
}

T:模板参数包,args叫做函数参数包
省略号作用:
1、声明一个包含0到任意个模板参数的参数包
2、在模板定义的右边,可以将参数包展开一个个独立的参数
c++11可以使用递归函数的方式展开参数包,获得可变参数的每个值。不过这个需要提供一个参数包展开函数和一个递归终止函数。
参数Args…在展开的过程中会递归调用自己,每调用一次,参数包中的参数就会少一个,直到所有参数都展开为止。

右值引用

1、临时对象的深拷贝

一个带有堆内存的类,必须提供一个深拷贝的拷贝函数。因为默认的拷贝构造函数是浅拷贝,会发生指针悬挂问题(两个对象的两个指针指向同一块内存,当函数作用结束后,两个对象分别调用析构函数,错误地将一块内存区析构两次)。
提供深拷贝地构造函数可以保证正确,但是会造成额外地性能损耗。
在这里插入图片描述
GetA函数会返回临时变量,然后通过这个临时变量拷贝构造一个新的对象a,临时变量在拷贝构造完成之后就销毁了。如果这个堆内存很大地话,拷贝构造的代价很大。也就是说每次产生临时变量都会造成额外地性能损失。

2、移动构造函数解决问题

A(A&& a) :m_ptr(a.m_ptr)
    {
    
    
        a.m_ptr = nullptr;
        cout << "move construct" << endl;
    }

move construct这个构造函数是移动构造函数,它的参数是一个右值引用类型A&&。
它仅仅是将指针的所有者转移到了另一个对象上,同时将参数对象a的指针置空。也就是说,这里仅仅做了浅拷贝,从而避免了临时变量的深拷贝问题。

为什么能够匹配到这个构造函数呢?
这个构造函数只能够接受右值参数,而函数的返回值就是右值,所以就会匹配到这个构造函数。A&&看作临时值的标识。

对于临时值我们仅仅需要做浅拷贝即可,无需再做深拷贝,从而解决了临时变量拷贝构造产生的性能损失问题。这就是移动语义

3、左值借助移动语义优化性能

我们可以使用std::move将左值转换为右值引用。
move是将对象资源的所有权从一个对象转移到另一个对象,只是转移,没有内存的拷贝,这就是所谓的move语义。
通常这样使用:
使用move将左值转换成右值引用。应用移动语义,调用移动构造函数。
我们需要记住:
如果一个对象内部有较大的内存或者动态数组,就很有必要写move语义的拷贝构造函数和赋值函数,避免无所谓的深拷贝,以提高性能

左值引用和右值引用本质作用都是减少拷贝提高效率。
右值引用可以弥补左值引用不足的地方,右值引用做参数和做返回值减少拷贝的本质是利用了移动构造和移动赋值。
左值引用:
做参数: void func(T x) -> void func(T& x) 减少了传参过程中的拷贝
做返回值: T func() -> T& func() 减少返回过程中的宝贝(返回对象除了作用域不存在了就不能使用传引用了)
右值引用:
做参数: void func(T&& x) : 使func内部不再将x拷贝构造到容器空间上,而是采用移动构造
做返回值:利用移动构造减少拷贝。

4、完美转发

右值引用会在第二次之后的参数传递过程中右值属性丢失,在下一层调用中会识别成左值。
C++11引入完美转发的概念,完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数

#include<iostream>
using namespace std;
void Fun(int& x)
{
    
    
	cout << "左值引用" << endl;
}
void Fun(int&& x)
{
    
    
	cout << "右值引用" << endl;
}

template<typename T>
void PerfectForward(T&& t) 
{
    
    
	Fun(std::forward<T>(t));
}

int main()
{
    
    
	//右值引用
	PerfectForward(10);
	int a;
	//左值引用
	PerfectForward(a);
	//右值引用
	PerfectForward(std::move(a));
	return 0;
}

在这里插入图片描述

lambda表达式

lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量,定义如下:
[capture] (params)mutable->return_type{statement}

[capture] :捕获列表,捕获上下文变量以供lambda使用。编译器根据[]符号来判断接下来的代码是否是lambda函数。
(params):参数列表,与普通函数的参数列表一致,如果不需要传递参数,可以连同括号一起省略
mutable:修饰符,默认情况下lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略
return_type:返回值类型
{statement}:函数体,内容与普通函数一样,除了可以使用参数之外,还可以使用所捕获的变量。

lambda表达式与普通函数最大的不同就是它可以通过捕获列表访问一些上下文的数据。

[var] : 表示以 值传递方式 捕获 变量var
[=] : 表示以 值传递方式 捕获 所有父作用域的变量(包括this)
[&var] : 表示以 引用传递方式 捕获 变量var
[&] : 表示以 引用传递方式 捕获 所有父作用域的变量(包括this)
[this] : 表示以 值传递方式 捕获当前的this指针

lambda的类型被定义为“闭包”的类,通常用于stl库中。在某些场景下用于简化仿函数的使用,同时lambda作为局部函数,也会提高复杂代码的开发速度。可以在函数内部重用代码,不需要费心设计接口。
下面是示例代码:

#include<iostream>
using namespace std;

int main()
{
    
    
	int a = 0, b = 1;
	//实现a+b的lambda表达式
	auto add1 = [](int x, int y)->int {
    
    return x + y;};
	cout << add1(a, b) << endl;

	auto add2 = [a, b]()->int {
    
    return a + b;};
	cout << add2() << endl;

	cout << a << " " << b << endl;
	//实现a和b的交换
	auto swap1 = [](int& x, int& y)
	{
    
    
		int tmp = x;
		x = y;
		y = tmp;
	};
	swap1(a, b);
	cout << "swap1() " << a << " " << b << endl;
	auto swap2 = [&a, &b]()mutable
	{
    
    
		int tmp = a;
		a = b;
		b = tmp;
	};
	swap2();
	cout << "swap2() "<< a << " " << b << endl;
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42604176/article/details/114643438