施磊C++ | 进阶学习笔记 | 3.绑定器和函数对象、lambda表达式

news2024/11/25 12:32:43

三、绑定器和函数对象、lambda表达式

文章目录

    • 三、绑定器和函数对象、lambda表达式
      • 3.1模板的完全特例化和非完全(部分)特例化
        • 1.完全特例化和非完全(部分)特例化
        • 2.模板的实参推演
      • 3.2 C++ STL中的绑定器
        • bind1st
        • bind2nd
        • 自己实现一个bind1st
      • 3.3 C++11从Boost库中引入了bind和function函数对象机制
        • 1.function函数对象类型的应用示例(function基础)
        • 2.fucntion函数对象类型的实现原理
        • 3.bind
        • 4.bind 和 function实现线程池
      • 3.4 lambda表达式 底层依赖函数对象机制实现的
        • 1.lambda概述
        • 2.lambda表达式的语法
            • lambda匿名函数中的[外部变量]
        • 3.lambda的应用实践
          • 1.泛型算法之中
          • 2.既然lambda只能用在语句中,如果想跨语句使用之前定义好的lambda表达式怎么办?有什么类型表达lambda?

3.1模板的完全特例化和非完全(部分)特例化

1.完全特例化和非完全(部分)特例化
template<typename T>
class Vector
{
public:
	Vector () { cout << "call Vector template init" << endl; }
};
// 下面这个是对char*类型提供的完全特例化版本 
    //1.必须得有上面那个才能提供下面这个,光下面这个也不行
    //2. "<>"这是语法不能省略
template<>
class Vector<char*>
{
public:
	Vector () { cout << "call Vector<char*> init" << endl; }

};
//针对指针类型提供的部分特例化版本 
template<typename T>//我们并不知道是哪个类型的指针,所以还是要写typename T
class Vector<T*>
{
public:
	Vector () { cout << "call Vector<T*> init" << endl; }
};
//针对函数指针类型(有返回值,有两个形参变量的函数)提供的部分特例化版本 
template<typename R,typename A1,typename A2>
class Vector<R(*)(A1,A2)>
{
public:
	Vector () { cout << "call Vector<R(*)(A1,A2)> init" << endl; }
};
//完全特例化
template<>
class Vector<int(*)(int,int)>

//部分特例化更灵活一点
    
//函数类型部分特例化    
template<typename R,typename A1,typename A2>
class Vector<R(A1,A2)>
{
public:
	Vector () { cout << "call Vector<R(A1,A2)> init" << endl; }
};
int sum(int a,int b) {return a+b;}
int main()
{
    Vector<int> vec1;//从原模板进行实例化
    Vector<char *> vec2;//使用完全特例化版本
    Vector<int *> vec3;//使用针对指针的部分特例化版本
    Vector<int(*)(int,int)>vec4;//使用针对指针的特例化版本 这个类型相当于上面sum函数类型(函数指针),只是同一个类型,不是指sum函数
    Vector<int(int,int)>vec4;//这是函数类型,不是函数指针类型,匹配的是函数类型的模板进行实例化
    
    //函数类型
    typedef int (*PFUNC1) (int, int);
    PFUNC1 pfunc1 = sum;
    cout << pfunc1(10, 20) << endl;
	//函数指针类型
    typedef int PFUNC2 (int, int);
    PFUNC2 *pfunc2 = sum;
    cout << (*pfunc2)(10, 20) << endl;
    
    //注意区分两者
	return 0;
}

先看有没有完全特例化,没有看部分特例化,最后看原模板

2.模板的实参推演

typeid().name用来得到传入参数的类型

向上面一样,我们如果得到的T是

int (*)(int,int)或者int(int,int)

那完全没什么作用,就只是知道了,而现在可以通过实参推演,获得各个部分是什么类型,以下面代码为例

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

//获得普通类型
template<typename T>
void func(T a)
{
	cout << typeid(T).name() << endl;
}

int sum(int a, int b) { return a + b; }

template<typename R,typename A1,typename A2>//指针对函数指针
void func2(R (*a)(A1,A2))
{
	cout << typeid(R).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}
//int,int,int

class test
{
public:
	int sum(int a, int b) { return a + b; }
};

template<typename R, typename T,typename A1, typename A2>//针对类成员函数
void func3(R(T::*a)(A1, A2))
{
	cout << typeid(R).name() << endl;
	cout << typeid(T).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}
//int,class test,int,int
//可以用这些进行变量定义

int main()
{
	func(10);
	func("aaa");
	func(sum);
	cout << "----" << endl;
	func2(sum);
	cout << "----" << endl;
	func3(&test::sum);
	return 0;
}

运行结果:

image-20241012182940976

从结果可以看出,我们可以获得各个类型

3.2 C++ STL中的绑定器

bind1stbind2nd是C++标准库中的两个函数适配器,它们的主要作用是将一个二元函数对象(即接受两个参数的函数对象)转换为一元函数对象(即接受一个参数的函数对象)。这两个函数适配器的区别主要在于它们绑定的是二元函数对象的哪一个参数。

只能用于二元

bind1st
  • 功能:bind1st函数接受一个二元函数和一个值,返回一个新的函数对象。这个新的函数对象将二元函数的第一个参数绑定为给定的值,因此调用时只需要提供第二个参数。
  • 使用场景:当你希望将一个二元函数的第一个参数固定为某个值时,可以使用bind1st。这样,你就可以得到一个一元函数,其行为类似于原二元函数但第一个参数已被绑定。
bind2nd
  • 功能:与bind1st类似,bind2nd函数也接受一个二元函数和一个值,但返回的新函数对象将二元函数的第二个参数绑定为给定的值。因此,调用时只需要提供第一个参数。
  • 使用场景:当你希望将一个二元函数的第二个参数固定为某个值时,可以使用bind2nd。这样,你也可以得到一个一元函数,但其行为是原二元函数且第二个参数已被绑定。
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<ctime>
using namespace std;

void printvec(vector<int>& v)
{
	for (auto c : v)
		cout << c << " ";
	cout << endl;
}

int main()
{
	vector<int> vec;
	srand(time(nullptr));
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
	printvec(vec);
	//从小到大
	sort(vec.begin(), vec.end());
	printvec(vec);
	//从大到小
	sort(vec.begin(), vec.end(),greater<int>());
	printvec(vec);
	//greater a>b 从大到小
	//less a<b 从小到大

	/*bind绑定器
	把70按顺序插入到vec容器中 找第一个小于70的数字,那只传入一个参数,就没办法用gerater或者less了
	所以可以用绑定器 绑定器 + 二元函数对象 => 一元函数对象
	bind1st:+ greater bool operator()(70,const _Ty& _Right)
	bind2nd:+ less bool operator()(const _Ty& _Left,70)
	*/
	auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70));
	if (it1 != vec.end())
		vec.insert(it1, 70);
	printvec(vec);
	auto it2 = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 60));
	if (it2 != vec.end())
		vec.insert(it2, 60);
	printvec(vec);

	return 0;
}
自己实现一个bind1st
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<ctime>
using namespace std;

template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare comp)
{
	for (; first != last; first++)
	{
		if (comp(*first))//comp.operator()(*fist) comp的()重载函数传入参数为*first
		{
			return first;
		}
	}
	return last;
}


template<typename Compare, typename T>
class _mybind1st
{
public:
	_mybind1st(Compare comp,T val):_comp(comp),_val(val){}

	bool operator()(const T& second)
	{
		return _comp(_val, second);
	}
private:
	Compare _comp;
	T _val;
};

template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare comp, const T& val)
{
	return _mybind1st<Compare, T>(comp, val);
}

void printvec(vector<int>& v)
{
	for (auto c : v)
		cout << c << " ";
	cout << endl;
}

int main()
{
	vector<int> vec;
	srand(time(nullptr));
	for (int i = 0; i < 20; i++)
		vec.push_back(rand() % 100 + 1);
	printvec(vec);
	//从小到大
	sort(vec.begin(), vec.end());
	printvec(vec);

	auto it1 = find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 70));
	if (it1 != vec.end())
		vec.insert(it1, 70);
	printvec(vec);

	return 0;
}

过程说明:

  1. 创建 _mybind1st 对象
    当调用 mybind1st(greater<int>(), 70) 时,会创建一个 _mybind1st<greater<int>, int> 类型的临时对象。这个对象内部存储了 greater<int> 的一个实例(作为 _comp 成员变量)和整数 70(作为 _val 成员变量)。
  2. _mybind1st 对象传递给 my_find_if
    然后,这个 _mybind1st 对象被传递给 my_find_if 函数作为 comp 参数。
  3. my_find_if 中调用 comp(*first)
    my_find_if 的循环中,当调用 comp(*first) 时,实际上是在调用 _mybind1st 对象的 operator() 成员函数。这是因为 _mybind1st 对象是一个函数对象,它重载了 operator() 以使其表现得像一个函数。
  4. _mybind1stoperator() 实现
    _mybind1stoperator() 中,代码是 return _comp(_val, second);。这里,_comp 是存储的 greater<int> 对象,_val70,而 second*first(即当前正在检查的元素)。因此,这个调用实际上是在检查当前元素 *first 是否小于 70(因为 greater<int>operator() 检查第一个参数是否大于第二个参数,而这里我们是在用 _val(即 70)作为第一个参数,*first 作为第二个参数)。

补充内容:

greater<int>是C++标准库中的一个模板类,用于表示两个整数之间的“大于”关系。当实例化greater<int>时,会得到一个函数对象,该函数对象重载了operator(),接受两个int类型的参数,并返回第一个参数是否大于第二个参数的布尔值。

例如:

greater<int> comp;  
bool result = comp(5, 3); // result 为 true,因为 5 > 3

3.3 C++11从Boost库中引入了bind和function函数对象机制

1.function函数对象类型的应用示例(function基础)

绑定器,函数对象,lambda表达式 他们只能在一条语句中使用

function作用:把上面这三个的类型留下来,说得更清楚点就是再给他们一个名字然后方便之后通过调用给的名字来使用函数对象或者lambda的功能

function基础:

1.用函数类型实例化function
2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数

具体实例:

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

void hello1()
{
	cout << "hello world" << endl;
}

void hello2(string str)
{
	cout << str << endl;
}

int sum(int a, int b)
{
	return a + b;
}

class test
{
public:
	void hello2(string str)
	{
		cout << str << endl;
	}
};

int main()
{
	//从function的类模板定义处,看到希望用一个函数类型实例化function

	function<void()> func1 = hello1;

	//1.func1.operator() => hello1 先调用func1的()重载函数,然后调用包装的hello1
	func1();

	//2.人原来有参数,你写的时候就要加上参数,传入的时候也要传入
	function<void(string)> func2 = hello2;
	func2("hello hello2");

	//有返回值的
	function<int(int, int)> func3 = sum;
	cout << func3(20, 30) << endl;

	//3.lambda的类型保留 注意结尾不要忘了加分号
	function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };
	cout<<func4(100, 200)<<endl;

	//4.成员函数的类型保留
	//注意点1:在原来的参数列表要多加一个test*
	// 注意点2:调用func5的时候要借助该类对象的指针,这里就构建一个临时对象取地址当做指针来用
	function<void(test*, string)> func5 = &test::hello2;
	func5(&test(), "call test::hello");

	//成员函数指针和普通函数指针不同
	//普通指针随便调用就行,但成员函数前面必须要有一个作用域,所以必须依赖一个对象,这也是调用func5要加一个test*的原因
	//成员函数一经编译参数都会多一个指向当前类型的this指针

	//void (*print)(string str)
	//void (test::*print)(striing str)

	return 0;
}

结合例子来看:

1.func1.operator() => hello1 先调用func1的()重载函数,然后调用包装的hello1

2.人原来有参数,你写的时候就要加上参数,传入的时候也要传入

3.lambda的类型保留 注意结尾不要忘了加分号

4.成员函数的类型保留
注意点1:在原来的参数列表要多加一个test*
注意点2:调用func5的时候要借助该类对象的指针,这里就构建一个临时对象取地址当做指针来用

  1. 成员函数指针和普通函数指针不同
    普通指针随便调用就行,但成员函数前面必须要有一个作用域,所以必须依赖一个对象,这也是调用func5要加一个test*的原因
    成员函数一经编译参数都会多一个指向当前类型的this指针

    void (*print)(string str)
    void (test::*print)(striing str)
    
2.fucntion函数对象类型的实现原理

实例:

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


void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }

//原模板
template<typename Fty>
class myfunction{};

//模板的特例版本 针对hello
template<typename R,typename A1>
class myfunction<R(A1)>
{
public:
	using PFUNC = R(*)(A1);
	myfunction(PFUNC pfunc):_pfunc(pfunc){}
	R operator()(A1 arg)
	{
		return _pfunc(arg);
	}
private:
	PFUNC _pfunc;
};

//模板的特例版本 针对sum
template<typename R, typename A1, typename A2>
class myfunction<R(A1,A2)>
{
public:
	using PFUNC = R(*)(A1,A2);
	myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
	R operator()(A1 arg1,A2 arg2)
	{
		return _pfunc(arg1,arg2);
	}
private:
	PFUNC _pfunc;
};

int main()
{
	myfunction<void(string)> func1 = hello;
	func1("hello world");
	
	myfunction<int(int,int)> func2 = sum;
	cout<<func2(10, 20) << endl;

	return 0;
}

那肯定会觉得,那这得写多少代码才可以写完,但其实不需要。C+11中提供了可变参的类型参数:包(arg…)

可以理解为把你传进入的所有参数,依次一个一个的传入

感兴趣的读者可以去这里看看相关知识点,这里不再赘述。

C++11 新特性 学习笔记-CSDN博客

通过可变参数实现:

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


void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }

template<typename Fty>
class myfunction{};

template<typename R, typename... A>
class myfunction<R(A...)>
{
public:
	using PFUNC = R(*)(A...);
	myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
	R operator()(A... arg)
	{
		return _pfunc(arg...);//相当于return sum(10,20);
	}
private:
	PFUNC _pfunc;
};

int main()
{
	myfunction<void(string)> func1 = hello;
	func1("hello world");
	
	myfunction<int(int,int)> func2 = sum;
	cout<<func2(10, 20) << endl;

	return 0;
}

可以当做是一个可变参数模板的一个实例进行记忆。

3.bind

bind绑定器返回的也是函数对象

#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<ctime>
#include<typeinfo>
using namespace std;
using namespace placeholders;

void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }

class test
{
public:
	int sum(int a, int b) { return a + b; }
};

int main()
{
	//1.bind是函数模板 可以自动推演模板类型参数
	bind(hello, "hello bind")();
	cout << bind(sum, 10, 20)() << endl;
	cout<<bind(&test::sum, test(), 20, 30)()<<endl;

	//2.参数占位符 最多绑定20个参数
	bind(hello, placeholders::_1)("hello bind2");
	//using namespace placeholders; 有了这句话就可以不写前面的作用域,直接写_1,_2最多20个
	cout<<bind(sum, _1, _2)(100, 200)<<endl;

	//3.绑定器除了当前语句无法继续使用 解决方法:function
	//此处把bind返回的绑定器就复用起来了
	function<void(string)> func1 = bind(hello,_1);
	func1("hello china");
	func1("hello a");
	func1("hello b");


	return 0;
}
4.bind 和 function实现线程池
#include <iostream>
#include <functional>
#include <vector>
#include <thread>
using namespace std;

class Thread
{
public:
    Thread(function<void()> func) : _func(func) {}
    thread start() {
        thread t(_func);
        return t;
    }
private:
    function<void()> _func;
};

class ThreadPool
{
public:
    ThreadPool() {}
    ~ThreadPool() {
        //释放Thread对象占用的堆资源
        for (int i = 0; i < _pool.size(); i++) {
            delete _pool[i];
        }
    }
    //开启线程池
    void startPool(int size)
    {
        for (int i = 0; i < size; i++) {
            //本身不可以是成员方法,但是可以用bind做到,i作为线程编号
            _pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, i)));
        }
        for (int i = 0; i < size; i++) {
            _handler.push_back(_pool[i]->start());
        }
        for (thread& t : _handler) {
            t.join();
        }
    }
private:
    vector<Thread*> _pool;
    vector<thread> _handler;
    //把runInThread这个成员方法充当线程函数
    void runInThread(int id) {
        cout << "call runInThread id: " << id << endl;
    }
};

int main()
{
    ThreadPool pool;
    pool.startPool(10);

    return 0;
}

3.4 lambda表达式 底层依赖函数对象机制实现的

1.lambda概述

函数对象的的升级版----lambda

函数对象的缺点:

灵活性太差了,需要定一个类出来,但其实实际上我可能只需要在泛型算法里面用一次就不用了

2.lambda表达式的语法

语法:

[捕获外部变量](形参列表)->返回值{操作代码};

注意操作代码后面也有分号,句子结束也有分号,不要忘了,不要少写

如果没有返回值"->返回值"这部分可以省略

关于形参列表部分,如果参数,关键字,返回值一个都没有,那小括号可写可不写,如果有一个那就得写小括号

形参列表对应的是函数对象中operator()()的形参列表

lambda匿名函数中的[外部变量]

外部变量指的是和lambda在一个作用域的变量,写[=]把同一作用域和更外层的变量拷贝一个副本

这个应该对应函数对象的类里面的成员变量

外部变量格式功能
[]空方括号表示当前 lambda 匿名函数中不导入任何外部变量。
[=]只有一个 = 等号,表示以值传递的方式导入所有外部变量;
[&]只有一个 & 符号,表示以引用传递的方式导入所有外部变量;
[val1,val2,…]表示以值传递的方式导入 val1、val2 等指定的外部变量,同时多个变量之间没有先后次序;
[&val1,&val2,…]表示以引用传递的方式导入 val1、val2等指定的外部变量,多个变量之间没有前后次序;
[val,&val2,…]以上 2 种方式还可以混合使用,变量之间没有前后次序。
[=,&val1,…]表示除 val1 以引用传递的方式导入外,其它外部变量都以值传递的方式导入。
[this]表示以值传递的方式导入当前的 this 指针。

注意,单个外部变量不允许以相同的传递方式导入多次。例如 [=,val1] 中,val1 先后被以值传递的方式导入了 2 次,这是非法的。

image-20241012215219186

代码示例:

#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<ctime>
#include<typeinfo>
using namespace std;
using namespace placeholders;

template<typename T=void>
class testLambda01
{
public:
	testLambda01(){}
	void operator()()
	{
		cout << "hello world" << endl;
	}
};

template<typename T = int>
class testLambda02
{
public:
	testLambda02() {}
	int operator()(int a,int b)
	{
		return a + b;
	}
};

template<typename T = int>
class testLambda03
{
public:
	testLambda03(int a,int b):ma(a),mb(b) {}
	//常方法里面不能修改成员变量
	//1.成员变量加mutable
	//2.lambda加关键字mutable
	void operator()() const
	{
		int tmp = ma;
		ma = mb;
		mb = tmp;
	}
private:
	mutable int ma;
	mutable int mb;
};

int main()
{
	int a = 10;
	int b = 20;
	auto func3 = [a, b]()mutable
		{
			int tmp = a;
			a = b;
			b = tmp;
		};

	func3();
	cout << "a " << a << "b " << b << endl;

	testLambda03<> t3(a, b);
	t3();
	cout << "a " << a << "b " << b << endl;
	return 0;
}



template<typename T = int>
class testLambda04
{
public:
	testLambda04(int &a, int &b):ma(a),mb(b) {}
	//常方法里面不能修改成员变量
	//1.成员变量加mutable
	//2.lambda加关键字mutable
	void operator()() const
	{
		int tmp = ma;
		ma = mb;
		mb = tmp;
	}
private:
	int &ma;
	int &mb;
};

int main()
{
	auto func1 = []()->void {cout << "hello world"<<endl; };
	func1();

	auto func2 = [](int a, int b)->int {return a + b; };

	cout << func2(20, 30) << endl;

	//func1的函数对象版本
	testLambda01<> t1;
	t1();
	//func2的函数对象版本
	testLambda02<> t2;
	cout<<t2(20,30)<<endl;
	
	//虽然实现了func3lambda对应的函数对象,但是a,b根本上还是没变(因为是值传递,不是引用),没有实现交换这个功能
	int a = 10;
	int b = 20;
	auto func3 = [a, b]()mutable
		{
			int tmp = a;
			a = b;
			b = tmp;
		};

	func3();
	cout <<"a "<< a <<"b "<< b << endl;

	testLambda03<> t3(a,b);
	t3();
	cout << "a " << a << "b " << b << endl;

	//实现交换功能,按照引用来传递参数,lambda后面的mutable也不用加了
	//函数对象里面的mutable也不用加了 因为交换改变的是ma,mb的内存里面的东西
	/*int a = 10;
	int b = 20;
	auto func4 = [&a, &b]()
		{
			int tmp = a;
			a = b;
			b = tmp;
		};

	func4();
	cout << "a " << a << " b " << b << endl;

	testLambda04<> t4(a, b);
	t4();
	cout << "a " << a << " b " << b << endl;*/
	return 0;
}

Q:为什么在类中换成引用类型就不需要mtable来修饰成员变量了?

在C++中,当成员变量是引用类型时,即使在const成员函数内部,我们也可以通过这些引用来修改它们所引用的外部对象的状态。这是因为const成员函数中的const关键字限制了成员变量本身的“可修改性”(如果成员变量不是引用或指针的话),但它并不限制通过成员变量(如果它们是引用或指针)所间接访问的对象的可修改性。

具体来说,当一个成员函数被声明为const时,这意味着该成员函数不能修改其所属对象的任何非静态成员变量(除非这些变量被声明为mutable)。然而,这一规则并不适用于引用类型的成员变量,因为引用本身并不是对象,它只是对象的一个别名。当我们说“不能修改成员变量”时,实际上是指不能改变成员变量所引用的对象(如果成员变量是对象的话)或成员变量所指向的地址(如果成员变量是指针的话)。但是,如果成员变量是引用,并且它引用了一个外部对象,那么我们完全可以通过这个引用来修改那个外部对象的状态。

换句话说,const成员函数中的const性仅仅保证了成员函数不会改变其所属对象的“身份”(即不会改变成员变量的值,如果成员变量是值类型的话),但并不保证不会改变成员变量所引用的外部对象的“状态”。

3.lambda的应用实践
1.泛型算法之中

就使用这一下,不用麻烦的写函数对象

//1.vector从大到小排序的lambda排序
vector<int> v;
sort(v.begin(),v.end(),[](int a,int b)->bool
     {
         return a>b;
		})
//2.找到第一个比65小的数字的位置插入65到这里
auto it=find_id(v.begin(),v.end(),[](int a)->bool{return a<65;});
if(it!=v.end())
    	v.insert(it,65);

为什么a,b在()而不是[]里面?

在C++中,当你使用lambda表达式作为sort函数的比较函数时,lambda表达式的参数(在这个例子中是ab)是通过operator()函数传入的,而不是通过捕获列表[]传入的。这是因为捕获列表[]用于指定哪些外部变量应该被捕获到lambda表达式的内部作用域中,而不是用于接收函数参数。

2.既然lambda只能用在语句中,如果想跨语句使用之前定义好的lambda表达式怎么办?有什么类型表达lambda?

解决方法:用函数对象function表示lambda的类型,lambda->函数对象

class Data
{
public:
	Data(int val1=10,int val2=10):ma(val1),mb(val2){}
    bool operator>(const Data &data) const { return ma > data.ma; }
	bool operator<(const Data &data) const { return ma < data.ma; }
    int ma;
    int mb;
}
int main ()
{
    
    //1.function存储类型
    map<int, function<int(int, int)>> caculateMap;
    caculateMap [1] = [] (int a, int b)->int {return a + b; };
    caculateMap [2] = [] (int a, inth)>int {return a - b; };
    caculateMap [3] = [] (int a, int b)->int {return a * b; };
    caculateMap [4] = [] (int a, int b)->int {return a / b; };

    cout << "选择:";
    int choice;
    cin >> choice;
    cout << "10 + 15:" << caculateMap[choice] (10, 15) << endl;

    int main ()

	// 2.智能指针自定义删除器
    unique_ptr<FILE,function<void (FILE *)>>
    pti1 (fopen ("data.txt", "w"), [] (FILE *pf) {fclose(pf); });
    
	//3.优先级队列 默认要进行比较,因为底层是大根堆 但是>和<运算符重载有点麻烦且灵活性很差,每次都要改动
    priority_queue<Data> queue;
    queue.push(Data(10,20));
    queue.push(Data(15,15));
    queue.push(Data(20,30));
    //修改后,加入了比较函数,比较灵活
    using PFUNC=function<bool(Data&,Data&)>;
    priority_queue<Data,vector<Data>,PFUNC> 
        q([](Data &d1,Data &d2)->bool
        {
            return d1.ma>d2.ma;
		}
    );
    q.push(Data(10,20));
    q.push(Data(15,15));
    q.push(Data(20,30));
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习——线性神经网络(三、线性回归的简洁实现)

目录 3.1 生成数据集3.2 读取数据集3.3 定义模型3.4 初始化模型参数3.5 定义损失函数3.6 定义优化算法3.7 训练 在上一节中&#xff0c;我们通过张量来自定义式地进行数据存储和线性代数运算&#xff0c;并通过自动微分来计算梯度。实际上&#xff0c;由于数据迭代器、损失函数…

基于深度学习的西红柿成熟度检测系统

简介&#xff1a; 基于深度学习技术的西红柿成熟度检测系统是一种利用人工智能算法对西红柿成熟程度进行自动识别和分类的智能系统。该系统通过采集西红柿的图像数据&#xff0c;运用深度学习模型对图像中的西红柿进行特征提取和分析&#xff0c;从而实现对西红柿成熟度的准确判…

【C】printf()与scanf()详介以及如何在VS中使用scanf(保姆级详细版)

printf() 基本用法 printf()的作用是将参数文本输出到屏幕,它名字里面的f 代表 format(格式化)&#xff0c;表示可以定制输出文本的格式。 1 #include <stdio.h>//standard input output标准输入输出操作 2 int main() 3{ 4 printf("Hello World"); 5 retu…

DVWA CSRF 漏洞实践报告

1. 漏洞简介 CSRF&#xff08;跨站请求伪造&#xff09;是一种攻击&#xff0c;使得攻击者能够以受害者的身份执行非预期的操作。在靶场DVWA中&#xff0c;我将尝试通过CSRF漏洞更改管理员密码。 2. 实验环境 DVWA版本&#xff1a;DVWA-old浏览器&#xff1a;火狐默认管理员账…

QtModel

QModelIndex index1 model->index(row,column,QModelIndex());QModelIndex index2 model->index(row.column,index2); QSqlQuery::size() 仅在使用了 QSqlQuery::exec() 后并且查询结果集的所有行都被读取时才有效。如果结果集很大或在使用游标的情况下&#xff0c;返回…

Linux 内核态,用户态,以及如何从内核态到用户态,交互方式有哪些

一、Linux 内核态&#xff0c;用户态 Linux 内核态&#xff0c;用户态&#xff0c;以及如何从内核态到用户态&#xff0c;我来说下我的理解 很多面试官&#xff0c;面试也是照搬照套&#xff0c;网上找的八股文面试题&#xff0c;面试的人也是背八股文&#xff0c;刚好背到了&…

全面讲解C++

数据类型 1.1 基本数据类型 1.1.1 整型&#xff08;Integer Types&#xff09; 整型用于表示整数值&#xff0c;分为以下几种类型&#xff1a; int&#xff1a;标准整数类型&#xff0c;通常为4字节&#xff08;32位&#xff09;。short&#xff1a;短整型&#xff0c;通常…

被装物联网系统|DW-S305系统是一套成熟系统

东识被装仓库管理系统&#xff08;智被装DW-S305&#xff09;作业管理软件系统包括收发管理、库房管理、库存统计、环境监测、预警管理、数据展示、系统管理等功能&#xff0c;主要功能如下&#xff1a; 收发管理&#xff1a;对库房收发物资进行管理&#xff0c;支持收发物单据…

通信工程学习:什么是TCP/IP(传输控制协议/互联网议)

TCP/IP&#xff1a;传输控制协议/互联网议 TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff0c;传输控制协议/互联网协议&#xff09;是互联网的基本协议&#xff0c;也是国际互联网络的基础。它不仅仅是一个协议&#xff0c;而是一个协议族&#…

Github 2024-10-13php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-13统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Vue项目2JavaScript项目1TypeScript项目1Blade项目1Coolify: 开源自助云平台 创建周期:1112 天开发语言:PHP, Blade协议类型:Apach…

算法题总结(十四)——贪心算法(上)

贪心算法 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心的套路&#xff08;什么时候用贪心&#xff09; 刷题或者面试的时候&#xff0c;手动模拟一下感觉可以局部最优推出整体最优&#xff0c;而且想不到反例&#xff0c;那么就试一试…

Vscode+Pycharm+Vue.js+WEUI+django火锅(五)Django的API

如果只是嫌弃Djanggo的前台不好&#xff0c;用vue替换&#xff0c;只要在Djanggo项目里面创建一个Vue项目文件夹&#xff0c;然后 1.修改urls.py 修改路由 2.修改settings.py中&#xff0c;增加templates内容指向vue文件夹 3.静态文件staticfile_dir中也添加vue文件夹 但因为我…

深圳大学-Java程序设计-选实验3 包及继承应用

实验目的与要求&#xff1a; 实验目的&#xff1a;熟悉面向对象编程中package,import等语句的使用。 实验要求&#xff1a; (1).编写一个计算机与软件学院类CSSE、一个研究所/中心类Institute和一个教学系类Department。CSSE类中包含有多个Institute类的实例和多个Department…

信息技术 04 WPS文字处理 图书订购单

信息技术 04 WPS文字处理 图书订购单 素材下载 信息技术 04 WPS文字处理 图书订购单链接&#xff1a;https://pan.baidu.com/s/1_S9HMfmiC6JJcjk4nO-tKg?pwdi304 提取码&#xff1a;i304 成品样图 题目 任务实现具体要求如下&#xff1a; ① 根据设计好的表格的结构&#…

基于 PyQt5 和 Matplotlib 的医学图像处理应用开发

1. 引言 在医学领域&#xff0c;图像处理是一项非常重要的技术&#xff0c;特别是在医学成像&#xff08;如MRI、CT扫描等&#xff09;的数据处理上&#xff0c;可以帮助医生更加准确地进行诊断。本项目基于 Python 的 PyQt5 图形用户界面框架与 Matplotlib 数据可视化库&…

Variational Auto-Encoder(VAE)缺少数学推导未完结版

VAE是Diffusion的基础&#xff0c;在其中将输入的图片数据编码到潜在空间后再解码出来。 略显复杂&#xff0c;博主结合李宏毅视频、网上一些讲解以及自己的理解将其总结如下&#xff1a; 一、什么是VAE VAE&#xff08;变量自编码器&#xff09;最早在以上两篇文章被提出。 …

yakit使用教程(四,信息收集)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 前言&#xff1a;yakit下载安装教程。 一&#xff0c;基础爬虫。 在新建项目或新建临时项目后&#xff0c;点击安全工具&#xff0c;点击基础爬虫。 此工具并不是为了爬取网站上的一…

【零散技术】MAC 安装多版本node

时间是我们最宝贵的财富,珍惜手上的每个时分 不同前端项目运行的node版本不一致&#xff0c;会导致无法运行&#xff0c;就像Odoo也需要依据版本使用对应的python环境。python 可以用 conda随时切换版本&#xff0c;那么Node可以吗&#xff1f;答案是肯定的。 1、安装 n&#x…

k8s-资源管理、实战入门

资源管理 一、资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 &#xff08;1&#xff09;kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务&#xff0c;所谓的部署服务&…

SpringBoot高校学科竞赛平台:性能优化与实践

3系统分析 3.1可行性分析 通过对本高校学科竞赛平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本高校学科竞赛平台采用SSM框架&#xff0c;JAVA作为开发语…