目录

一、lambda表达式

1.1lambda的引入

1.2lambda的用法

1.3lambda的捕捉列表

1.4lambad的底层

二、可变参数模板

三、包装器

包装器的价值:

包装器模板:

包装器的用法:

四、源代码


一、lambda表达式

1.1lambda的引入

lambda的作用 :替代函数指针或仿函数

我们先来复习一下sort的用法,sort是库里实现的排序

int main()
{
	vector<int> ve = { 14,5,2,13,23,41,25,28,19,20,10,50 };
	sort(ve.begin(), ve.end());
	for (auto e : ve)
	{
		cout << e << " ";
	}
	cout << endl;
}

如果我们想排降序呢?

我们要写一个仿函数Greater()传给sort()

struct Greater
{
	bool operator()(const int& a, const int& b)
	{
		return a > b;
	}
};
sort(ve.begin(), ve.end(),Greater());
	for (auto e : ve)
	{
		cout << e << " ";
	}
	cout << endl;

这样就既能实现升序,也能实现降序

那如果此时要排列类型,不是内置类型,而是自定义类型呢?

struct Goods
{
	string _name;  // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate) {
	}
};

这个自定义类型,我们可以按照ta的名字 ,价格 ,评价其中之一来排序

此时要想让他用什么标准来排序,就要传怎样的仿函数给sort()

struct ComparePriceLess//价格的升序
{
	bool operator()(const Goods & gl, const Goods & gr)
	{
	return gl._price < gr._price;
	} 
};
struct ComparePriceGreater//价格的降序
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};
int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), ComparePriceGreater());
}

这是按价格的降序

我们好像不管什么比较,只要能写个像这样仿函数传给sort就行

	sort(v.begin(), v.end(), ComparePriceGreater());

我们用仿函数是用的好好的,为什么要去用lambda

  • 我们自己的仿函数是写的意思比较明显,如果遇到仿函数写的不是明显的,就是光看ta那个名字看不出来ta是怎么比的
  • 如果要写很多比较的话,就要写很多种仿函数,会显得很复杂

1.2lambda的用法

lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement 
}

  • lambda表达式各部分说明
    [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来
    判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda
    函数使用
  • (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以
    连同()一起省略
  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
    性 ,使用该修饰符时,参数列表不可省略(即使参数为空)
  • ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回
    值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推
    导。
  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情

用法演示: 

auto less = [](int x, int y)->bool {return x > y; };//本身是个对象 
cout << less(1, 2) << endl;
auto max = [](int x, int y) {return x > y ? x : y; };
cout << max(2, 3) << endl;

把lambda加到sort中

vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), [](const Goods& a, const Goods& b){return a._price < b._price; });
sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._evaluate < b._evaluate; });
sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._name < b._name; });
sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._price> b._price; });

可以很明显看到它的函数体,可以看到它的内部是怎么比较的,而且几乎格式都一样,只需要改一下函数体中的东西

1.3lambda的捕捉列表

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用

  • [var]:表示值传递方式捕捉变量var
auto add1 = [x, y]() { 
		return x+y;
		};
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)

父作用域指包含lambda函数的语句块

 // 省略参数列表和返回值类型,返回值类型由编译器推导为int
    int a = 3, b = 4;   [=]{return a + 3; };
  • [&var]:表示引用传递捕捉变量var
auto swap1 = [&x, &y]() { //上面的是传引用捕捉, 这里要传引用,用引用的方式捕捉
		int tmp = x;        
		x = y;
		y = tmp;
		};
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
int a = 3, b = 4;
auto f = [&] { a += b; return  a + 3; };
cout << f() << endl;;
cout << a << "  " << b << endl;

  • [this]:表示值传递方式捕捉当前的this指针

法上捕捉列表可由多个捕捉项组成,并以逗号分割。
 比如:

  1. [=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
  2. [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量

 捉列表不允许变量重复传递,否则就会导致编译错误。 比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复

块作用域以外的lambda函数捕捉列表必须为空。
 e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者
非局部变量都 
 会导致编译报错。

f. lambda表达式之间不能相互赋值,即使看起来类型相同

我们再来看一下lambda的用法

int main()
{
	int x = 1, y = 0;
	double rate = 2.5;
	auto add1 = [](int x, int y) {return x + y; };
	auto swap1 = [x,y]() {//由于x和y传过来,这里形参列表相当于是const x, const y ,接受实参 ,x和y是 const 类型的是不允许修改的
		int tmp = x;
		x = y;
		y = tmp;
		};
	return 0;
}
  • 捕捉列表捕捉了变量之后,在函数里面是可以直接使用的
  • 捕捉列表捕捉了x ,y在这里发生交换
  • 由于x和y传过来,这里形参列表相当于是const x, const y ,接受实参 ,x和y是 const 类型的是不允许修改的

那我们这样写呢?

mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
性 ,使用该修饰符时,参数列表不可省略(即使参数为空)

auto swap1 = [x, y]()mutable{//mutable让捕捉的x,y可以改变了 ,这里的形参列表相当于是x, y ,但这里的x,y依旧是外面的x,y的拷贝,并没有说真正的修改了x,y
		int tmp = x;             //就跟普通函数作交换而不传引用
		x = y; 
		y = tmp;
		};
	
  • 其实mutable让捕捉的 x,y可以改变了 ,这里的形参列表相当于是x, y ,而不是const x,const y但这里的x,y依旧是外面的x,y的拷贝,并没有说真正的修改了x,y
  • 就跟普通函数作交换而不传引用

所以这里要传引用,用引用的方式捕捉 ,(但这里写的很怪,像是取地址一样)

auto swap1 = [&x, &y]() { //上面的是传引用捕捉, 这里要传引用,用引用的方式捕捉
		int tmp = x;        
		x = y;
		y = tmp;
		};
	swap1();

当然,我们也可以直接写在参数里面,加上引用用引用传参 是一样的 

auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = tmp;
		};
	swap1(a, b);

swap之前:

swap之后:

我们能否在lambda中调用其他函数呢?

void func()
{ }

int main()
{
	int a = 1, b = 0;
	double rate = 2.5;
	auto add1 = [](int x, int y) {return x + y; };
	
	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		func();//可以调全局的函数,调不了局部的函数
		};
	swap1(a, b);

	//怎样用局部的函数

lambda可以调全局的函数,调不了局部的函数,那怎样用局部的函数呢?

 此时刚好补充了捕捉列表的知识,我们可以用捕捉列表,捕捉局部的函数,供我们使用

auto swap1 = [add1](int& x, int& y) {
	int tmp = x;
	x = y;
	y = x;
	cout << add1(x, y) << endl;
	};
swap1(a, b);

1.4lambad的底层

lambad的底层是什么呢

int main()
{
	auto f1 = [](int x, int y) {return x + y; };
	auto f2 = [](int x, int y) {return x + y; };

	//f1 = f2;不能赋值,因为它俩的类型不一样

	cout << typeid(f1).name() << endl; 
	cout << typeid(f2).name() << endl;

	return 0;
}
  • lambad的底层生成了两个仿函数,它是个类,f1和f2 是仿函数对象,不过这个仿函数的类型名称(仿函数的名称)我们不知道,是由编译器生成的
  • 为什么用auto去接收 ,这里我们就能看到auto的好处 ,    仿函数的名称对我们来说并不重要,因为是通过算法随机的,就算通过typeid(f1).name()拿到类型,下一次也不一定是这个

我们可以看见它俩的类型


也就是这样的

class`int__cdecl main(void)'::`2'::<lambda 1>  f1
class`int__cdecl main(void)'::`2'::<lambda 2>  f2
  • 前面是仿函数的名称,后面是仿函数的对象

什么是类型呢?我们可以参考上面的代码

struct Goods
{
	string _name;  // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate) {
	}
};
//
struct ComparePriceLess
{
	bool operator()(const Goods & gl, const Goods & gr)
	{
	return gl._price < gr._price;
	} 
};
struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};

  • 这里很明显,仿函数的名字,也就是用那个结构体来实现的结构体的名字,就是它的类型

二、可变参数模板

在C++98/03,类模版和函数模版中只能含固定数量的模版参数,而C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,我们希望模板类或模板函数能处理任意类型和任意数量的数据,而不仅限于预定义的固定模板参数,所以就有了可变参数模板

下面就是一个基本可变参数的函数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args){}

参数args前面有省略号,所以它就是一个可变模版参数, 我们把带省略号(...)的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数

  • 如果说你传了一个参数。Args模板参数包就只有一个类型,Args用这个类型定义了一个形参args
  • 如果你传了两个参数,Args模板参数包就有两个类型,Agrs用这两个类型定义了两个形参在args参数包中

ps:

Args是传的参数的类型的包
args是对应类型的变量的包

来看一下常见的可变参数(c语言)

int main()
{
	int x = 0, y = 1;
	printf("%d,%d\n", x, y);
	return 0;
}

这是C语言的,我们来看一下C++的 ,C++需要一个模板的可变参数

  •  模板参数:传类型,想要几个传几个
  •  函数参数:传对象

使用带有可变模板参数的模板函数时,通常需要一个递归展开机制。 为此,模板函数的参数列表通常包含一个固定参数(用于提取首个参数)和一个参数包(接收剩余参数)

我们看一下具体用法

//模板可变参数
template<class...Args>
void ShowList(Args...args)
{ }

void _ShowList()
{
	cout << endl;
}

template <class T,class...Args>//value :固定参数(用于提取首个参数)args: 一个参数包(接收剩余参数
void _ShowList(T value, Args...args)//底层可以视为一个数组存储着数字,但又不能把ta当数组
{                                  //想要使用ta需要用上下文去扩展参数包
	cout << value << " "; 
	_ShowList(args...);
}

template<class...Args>
void ShowList(Args...args)
{ 
	_ShowList(args...);
}

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1, 2);
	ShowList(1, 2, 3);
	ShowList(1, 2, 3, string("xxxxx"));
	  
	return 0; 
}
  • value :固定参数(用于提取首个参数)args: 一个参数包(接收剩余参数
  • 每次调用 _ShowList,固定参数 T value 接收参数包 args... 中的第一个参数。
  • 剩余参数再次形成一个新的参数包 args...,传递给下一次递归调用。
  • 这个过程就像从一个袋子(参数包)中逐个取出参数,直到参数包为空

我们可以用这种方法来初始化Date类 

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date构造" << endl;
	}

	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "Date拷贝构造" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


template <class ...Args>
Date* Create(Args... args)
{
	Date* ret = new Date(args...);

	return ret;
}

int main()
{
	Date* p1 = Create();
	Date* p2 = Create(2023);
	Date* p3 = Create(2023, 9);
	Date* p4 = Create(2023, 9, 27);

	Date d(2023, 1, 1);
	Date* p5 = Create(d);//这里是拷贝构造

	return 0;
}

三、包装器

仿函数是为了方便来替代函数指针,lambda为了方便替代仿函数,那它们能不能统一使用呢

template<class F, class T>
T useF(F f, T x)//f 有可能是函数指针,仿函数,lambda,相当于一个模板,来实例化出三份不一样的函数
{
	static int count = 0;
	cout << "count: " << ++count << endl;
	cout << "count: " << &count << endl;

	return f(x);
}

double f(double i)// f 是函数指针
{
	return i / 2;
}

struct Functor
{
	double operator()(double d)//Functor是仿函数
	{
		return d / 3;
	}
};

int main()
{
	//函数名
	cout << useF(f, 11.11) << endl;
	//函数对象
	cout << useF(Functor(), 11.11) << endl;

	//lambda表达式
	cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;

打印结果

可以看到ta们这里的count的地址都是不一样的,说明他们实例化出了三份不同的函数

  • f 有可能是函数指针,仿函数,lambda,相当于一个模板,来实例化出三份不一样的函数

包装器的价值:

 我们来看这个使用场景 :   可调用对象存储到容器比如 vector <> 中,类型咋写,是写函数指针呢,还是仿函数对象呢,又或者是lambda呢,不确定

可能会有友友觉得写个仿函数对象或者函数指针不就行了吗?为什么要这么麻烦呢?

因为 ,如果我们只写仿函数的类型那我们只能传和存仿函数的对象,其他的类型都不能搞
而 包装器解决了,只能传单一类型和接收单一类型对象的问题,
有了包装器之后我们在容器中可以传和接收函数指针和仿函数以及lambda中的任意一个类型和对应的的对象

包装器模板:

// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

 
    Args…:被调用函数的形参(这里的形参怎么是类型啊):因为Args是模板参数,ta的形参就是类型
    模板参数:传类型,想要几个传几个
    函数参数:传对象

可能看这个看不懂,我们直接看他的用法

包装器的用法:

//function直接统一了函数指针,仿函数,lambda成了一份类型,这样ta只实例化出了一份函数
function < double(double) > f1 = f;
function < double(double) > f2 = [](double d)->double {return d / 4; };
function < double(double) > f3 = Functor();

////地址都一样 ,这样我们可以传和接收函数指针和仿函数以及lambda中的任意一个类型和对应的的对象
cout << useF(f1, 11.11) << endl;
cout << useF(f2, 11.11) << endl;
cout << useF(f3, 11.11) << endl;

地址都一样 ,这样我们可以传和接收函数指针和仿函数以及lambda中的任意一个类型和对应的的对象

可调用对象存储到容器 :

写法一:

function < double(double) > f1 = f;
function < double(double) > f2 = [](double d)->double {return d / 4; };
function < double(double) > f3 = Functor();

vector<function<double(double)>> v = { f1,f2,f3 };

写法二:

	vector<function<double(double)>> v1 = { f, [](double d)->double {return d / 4; },Functor() };

运行结果 :

四、源代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<functional>
using namespace std;

struct Greater
{
	bool operator()(const int& a, const int& b)
	{
		return a > b;
	}
};


int main()
{
	vector<int> ve = { 14,5,2,13,23,41,25,28,19,20,10,50 };
	sort(ve.begin(), ve.end());
	for (auto e : ve)
	{
		cout << e << " ";
	}
	cout << endl;
	sort(ve.begin(), ve.end(),Greater());
	for (auto e : ve)
	{
		cout << e << " ";
	}
	cout << endl;
}

struct Goods
{
	string _name;  // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate) {
	}
};
//
struct ComparePriceLess
{
	bool operator()(const Goods & gl, const Goods & gr)
	{
	return gl._price < gr._price;
	} 
};
struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};
//lambda的用法:
//lambda: 替代函数指针或仿函数
//lambda: 匿名函数对象 ,在函数内部,直接定义使用
int main()
{
	//ComparePriceLess ls;
	//cout <<typeid(ls).name() << endl;
	//ComparePriceGreater lss;
	//cout << typeid(lss).name() << endl;



	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };
	sort(v.begin(), v.end(), [](const Goods& a, const Goods& b){return a._price < b._price; });
	sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._evaluate < b._evaluate; });
	sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._name < b._name; });
	sort(v.begin(), v.end(), [](const Goods& a, const Goods& b) {return a._price> b._price; });
	//sort(v.begin(), v.end(), ComparePriceGreater());

	//auto less = [](int x, int y)->bool {return x > y; };//本身是个对象 
	//cout << less(1, 2) << endl;
	//auto max = [](int x, int y) {return x > y ? x : y; };
	//cout << max(2, 3) << endl;
}

void func()
{ }

int main()
{
	int a = 1, b = 0;
	cout << a <<"   " << b << endl;
	double rate = 2.5;
	//auto add1 = [](int x, int y) {return x + y; };
	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = tmp;
		};
	swap1(a, b);
	cout << a <<"   " << b << endl;
	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		func();//可以调全局的函数,调不了局部的函数
		};
	swap1(a, b);

	//怎样用局部的函数

	//引出捕捉列表
	auto add3 = [rate](int x, int y) {return (x + y)*rate; };


	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		};
	swap1(a, b);

	return 0;
}
//捕捉列表
int main()
{
	int x = 1, y = 0;
	double rate = 2.5;
	auto add1 = [](int x, int y) {return x + y; };
	//auto swap1 = [x,y]() {//由于x和y传过来,这里形参列表相当于是const x, const y ,接受实参 ,x和y是 const 类型的是不允许修改的
	//	int tmp = x;
	//	x = y;
	//	y = x;
	//	};
	auto swap1 = [x, y]()mutable{//mutable让捕捉的x,y可以改变了 ,这里的形参列表相当于是x, y ,但这里的x,y依旧是外面的x,y的拷贝,并没有说真正的修改了x,y
		int tmp = x;             //就跟普通函数作交换而不传引用
		x = y; 
		y = x;
		};
	auto swap1 = [&x, &y]() { //上面的是传引用捕捉, 这里要传引用,用引用的方式捕捉
		int tmp = x;        
		x = y;
		y = x;
		};
	swap1();
	return 0;
}
//ok,回到   //lambda怎样用局部的函数, 我们只需要捕捉一下局部的函数
int main()
{
	int a = 1, b = 0;
	double rate = 2.5;
	auto add1 = [](int x, int y) {return x + y; };
	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		};
	swap1(a, b);

	auto swap1 = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		func();//可以调全局的函数,调不了局部的函数
		};
	swap1(a, b);

	//怎样用局部的函数
	//捕捉一下局部的函数 
	auto swap1 = [add1](int& x, int& y) {
		int tmp = x;
		x = y;
		y = x;
		cout << add1(x, y) << endl;
		};
	swap1(a, b);

	return 0;
}



//lambad的底层是什么?
int main()
{
	//auto f1 = [](int x, int y) {return x + y; };
	//auto f2 = [](int x, int y) {return x + y; };

	////f1 = f2;不能赋值,因为它俩的类型不一样

	//cout << typeid(f1).name() << endl; 
	//cout << typeid(f2).name() << endl;

	////lambad的底层生成了两个仿函数,它是个类,f1是仿函数对象,不过这个仿函数的类型名称(仿函数的名称)我们不知道,是由编译器生成的

	//ComparePriceLess ls;
	//cout <<typeid(ls).name() << endl;
	//ComparePriceGreater lss;
	//cout << typeid(lss).name() << endl;

	int a = 3, b = 4;
	auto f = [&] { a += b; return  a + 3; };
	cout << f() << endl;;
	cout << a << "  " << b << endl;
	return 0;
}


 //可变参数列表
int main()
{
	//常见的可变参数(c语言)
	int x = 0, y = 1;
	printf("%d,%d\n", x, y);
	//C++需要一个模板的可变参数
	//模板参数:传类型,想要几个传几个
	//函数参数:传对象
	return 0;
}

//模板可变参数
template<class ...Args>//Args是一个模板参数包,args是一个函数形参参数包
void ShowList(Args...args)//这个参数包可以包含0到任意个模板参数
{
}

//如果说你传了一个参数。Args模板参数包就只有一个类型,Args用这个类型定义了一个形参args
//如果你传了两个参数,Args模板参数包就有两个类型,Agrs用这两个类型定义了两个形参
//Args是传的参数的类型的包
//args是对应类型的变量的包
//
//
//模板可变参数
template<class...Args>
void ShowList(Args...args)
{ }

void _ShowList()
{
	cout << endl;
}

template <class T,class...Args>
void _ShowList(T value, Args...args)//底层可以视为一个数组存储着数字,但又不能把ta当数组
{                                  //想要使用ta需要用上下文去扩展参数包
	cout << value << " "; 
	_ShowList(args...);
}

template<class...Args>
void ShowList(Args...args)
{ 
	_ShowList(args...);
}

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1, 2);
	ShowList(1, 2, 3);
	ShowList(1, 2, 3, string("xxxxx"));
	  
	return 0; 
}
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date构造" << endl;
	}

	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		cout << "Date拷贝构造" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};


template <class ...Args>
Date* Create(Args... args)
{
	Date* ret = new Date(args...);

	return ret;
}

int main()
{
	Date* p1 = Create();
	Date* p2 = Create(2023);
	Date* p3 = Create(2023, 9);
	Date* p4 = Create(2023, 9, 27);

	Date d(2023, 1, 1);
	Date* p5 = Create(d);//这里是拷贝构造

	return 0;
}
 

//复习一下lambad
int main()
{
	auto f1 = [](int x, int y) {return x + y; };
	auto f2 = [](int x, int y) {return x + y; };

	//f1 = f2;不能赋值,因为它俩的类型不一样

	cout << typeid(f1).name() << endl; 
	cout << typeid(f2).name() << endl;

  //为什么用auto去接收
	//lambda的底层生成了两个仿函数,它是个类,f1是仿函数对象,不过这个仿函数的类型名称(仿函数的名称)我们不知道,是由编译器生成的
  //这里就体现出了auto的好处,仿函数的名称对我们来说并不重要,因为是通过算法随机的,就算通过typeid(f1).name()
  //拿到类型,下一次也不一定是这个
	return 0;
}


//包装器
//仿函数是为了方便来替代函数指针,lambda为了方便替代仿函数,那它们能不能统一使用呢
template<class F, class T>
T useF(F f, T x)//f 有可能是函数指针,仿函数,lambda,相当于一个模板,来实例化出三份不一样的函数
{
	static int count = 0;
	cout << "count: " << ++count << endl;
	cout << "count: " << &count << endl;

	return f(x);
}

double f(double i)// f 是函数指针
{
	return i / 2;
}

struct Functor
{
	double operator()(double d)//Functor是仿函数
	{
		return d / 3;
	}
};

int main()
{
	//函数名
	cout << useF(f, 11.11) << endl;
	//函数对象
	cout << useF(Functor(), 11.11) << endl;

	//lambda表达式
	cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;

	//使用场景: 可调用对象存储到容器中,类型咋写,是写函数指针呢,还是函数对象呢,又或者是lambda呢,不确定//包装器可以解决
	//什么叫可调用对象?  
	// :想之前仿函数不就是在容器中的可调用对象

	/*包装器的价值
	包装器是个适配器,解决传可调用对象的类型的问题 
	如果我们只写仿函数的类型那我们只能传和存仿函数的对象,其他的类型都能搞
	包装器解决了,只能传单一类型和接收单一类型对象的问题,
	有了包装器之后我们可以传和接收函数指针和仿函数以及lambda中的任意一个类型和对应的的对象*/





	 
	 //class function<Ret(Args...)>;
	 //模板参数说明:
     //Ret: 被调用函数的返回类型
	 //Args…:被调用函数的形参(这里的形参怎么是类型啊):因为Args是模板参数,ta的形参就是类型
	 //模板参数:传类型,想要几个传几个
	 //函数参数:传对象
	
	//function直接统一了函数指针,仿函数,lambda成了一份类型,这样ta只实例化出了一份函数
	function < double(double) > f1 = f;
	function < double(double) > f2 = [](double d)->double {return d / 4; };
	function < double(double) > f3 = Functor();

	////地址都一样 ,这样我们可以传和接收函数指针和仿函数以及lambda中的任意一个类型和对应的的对象
	cout << useF(f1, 11.11) << endl;
	cout << useF(f2, 11.11) << endl;
	cout << useF(f3, 11.11) << endl;

	//写法一
	function < double(double) > f1 = f;
	function < double(double) > f2 = [](double d)->double {return d / 4; };
	function < double(double) > f3 = Functor();

	vector<function<double(double)>> v = { f1,f2,f3 };

	double n = 3.3;
	for (auto f : v)
	{
		cout << f(n++) << endl;
	}
	cout << endl;
	//写法二
	vector<function<double(double)>> v1 = { f, [](double d)->double {return d / 4; },Functor() };
	n = 3.3;
	for (auto f : v)
	{
		cout << f(n++) << endl;
	}
	
	return 0;
}
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐