C++ function函数对象和bind绑定器以及lambda表达式

一、bind1st和bind2nd

bind可用于给多元函数对象降元:bind + 二元函数对象 = 一元函数对象

/*
find_if需要一个一元函数对象作为参数,而greater是二元函数对象,这就需要用到绑定器
greater : a > b
less : a < b
*/
auto iter = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70)); // 返回第一个小于70的元素的位置

底层实现原理

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
#include <ctime>

using namespace std;

template<typename Container>
void show_container(Container& con) {
    for (typename Container::iterator it = con.begin(); it != con.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

template<typename Iterator, typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare comp) {
    // 这里传入的comp是封装好的一元函数对象
    for (Iterator iter = first; iter != last; iter++) {
        if (comp(*iter)) {
            return iter;
        }
    }
    return last;
}

template<typename Compare, typename T>
class MyBind1st {
public:
    // 这里传入的comp是一个二元函数对象
    MyBind1st(Compare comp, const T& val):_comp(comp), _val(val) {

    }
    // 通过重载operator()把二元函数对象封装为一元函数对象
    bool operator()(/*const T& first*/ const T& second) {
        // return _comp(first, _val);  // bind2nd写法
        return _comp(_val, second);
    }
private:
    Compare _comp;
    T _val;
};

template<typename Compare, typename T>
MyBind1st<Compare, T> my_bind1st(Compare comp, const T& val) {
    // 这里传入的comp是一个二元函数对象
    // 通过二元函数对象构造一元函数对象
    // 绑定器本身是一个函数对象,也就是重载了operator()
    return MyBind1st<Compare, T>(comp, val);
}

int main(int argc, char* argv[]){
    vector<int> vec;
    srand(time(nullptr));
    for (int i = 0; i < 10; i++) {
        vec.push_back(rand() % 100);
    }

    show_container(vec);
    sort(vec.begin(), vec.end(), greater<int>());
    // greater<int>()为二元函数对象,bind + 二元函数对象 = 一元函数对象
    auto iter = my_find_if(vec.begin(), vec.end(), my_bind1st(greater<int>(), 70)); // 返回第一个小于70的元素的位置

    if (iter != vec.end()) {
        vec.insert(iter, 70);
    }
    show_container(vec);
    return 0;
}

二、模板的完全实例化和部分实例化

优先级: 完全实例化 > 部分实例化

#include <iostream>

using namespace std;

template<typename T>
class Vector {
public:
	Vector() {
		cout << "call Vector" << endl;
	}
};

// 这是对char*类型的完全特例化版本
template<>
class Vector<char*> {
public:
	Vector() {
		cout << "call Vector<char*>" << endl;
	}
};

// 这是对指针类型提供的部分特例化版本
template<typename T>
class Vector<T*> {
public:
	Vector() {
		cout << "call Vector<T*>" << endl;
	}
};

// 针对有返回值、有两个形参的函数指针的部分特例化
template <typename R, typename A1, typename A2>
class Vector<R(*)(A1, A2)> {
public:
	Vector() {
		cout << "call Vector<R(*)(A1, A2)>" << endl;
	}
};

// 针对有返回值、有两个形参的函数的部分特例化
template <typename R, typename A1, typename A2>
class Vector<R(A1, A2)> {
public:
	Vector() {
		cout << "call Vector<R(A1, A2)>" << endl;
	}
};

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

int main() {
	Vector<int> vec1;
	Vector<int*> vec2;
	Vector<int(*)(int, int)> vec3;
	Vector<int(int, int)> vec4;

	typedef int(*PFUNC1)(int, int);
	PFUNC1 fun1 = add;  // PFUNC1是函数指针类型
	cout<<fun1(1, 2)<<endl;

	typedef int(PFUNC2)(int, int);
	PFUNC2* fun2 = add;  // PFUNC2是函数类型
	cout << (*fun2)(1, 2) << endl;
	return 0;
}
#include <iostream>
#include <typeinfo>

using namespace std;

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

template<typename T>
void fun1(T a) {
	cout << typeid(a).name() << endl;
}

template<typename R, typename A1, typename A2>
void fun2(R(*)(A1, A2)) {
	cout << typeid(R).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}

// 根据具体情况,不断细分,获取不同的类型
template<typename R, typename C, typename A1, typename A2>
void fun3(R(C::*)(A1, A2)) {
	// (C::*)表示类作用域下的函数,用函数指针表示
	cout << typeid(R).name() << endl;
	cout << typeid(T).name() << endl;
	cout << typeid(A1).name() << endl;
	cout << typeid(A2).name() << endl;
}

class Test {
public:
	int add2(int a, int b) {
		return a + b;
	}
};

int main() {
	fun1(add1);               // int (__cdecl*)(int,int)
	fun1(&Test::add2);        // int (__cdecl*)(int,int)
	fun2(add1); 
	fun3(&Test::add2);	
	return 0;
}

三、function函数对象的实现原理

一个实现了operator()的函数对象

#include <iostream>
#include <typeinfo>
#include <string>
#include <functional>

using namespace std;

// 先提供统一的模板
template<typename T>
class MyFunction {

};

// 根据具体情况细分(返回值和单个形参)
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;
};

int print(string str) {
	cout << str << endl;
	return str.size();
}

int main() {
	MyFunction<int(string)> func(print);
	int len = func("hello world"); //func.operator()("hello world")
	cout << len << endl;
	return 0;
}

匹配可变参数的类:...

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...);
	}

private:
	PFUNC _pfunc;
};

四、bind

通过前面的学习可以知道,绑定器bind返回的也是一个函数对象,功能比bind1st和bind2nd强大

#include <iostream>
#include <functional>
#include <string>
#include <typeinfo>

using namespace std;

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

void add1(int a, int b) {
	cout << a + b << endl;
}

class Test {
public:
	void add2(int a, int b) {
		cout << a + b << endl;
	}
};

int main() {
	bind(show, "hello")();  // bind(show, "hello")返回的是function<void()>函数对象
	bind(add1, 1, 2)();     // bind(add1, 1, 2)返回的是function<void()>函数对象
	bind(&Test::add2, Test(), 1, 2)();

	// 使用类型占位符,后续调用时再传
	// bind(show, placeholders::_1)返回的是function<void(string)>函数对象
	bind(show, placeholders::_1)("hello"); 
	// 最多绑定_20参数
	// bind(add1, placeholders::_1, placeholders::_2)返回的是function<void(int, int)>函数对象
	function<void(int, int)> func = bind(add1, placeholders::_1, placeholders::_2);
	func(1, 2);
	return 0;
}

bind其实就是调用了_Binder的构造方法,用构造方法中传入的函数show以及show的形参hello初始化数据成员,函数对象func调用operator()时,在operator()内会把数据成员传给函数对象进行调用,这就是bind的实现原理

五、lambda表达式

作用: 主要是为了避免使用一个函数对象的时候还去定义一个类,lambda表达式产生的对象用完就销毁

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

#include <iostream>
#include <functional>
#include <string>
#include <typeinfo>
#include <vector>

using namespace std;

template<typename T = void>
class Test1 {
public:
	Test1() {

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

template<typename T = int>
class Test2 {
public:
	Test2() {

	}
	T operator()(T a, T b) const{
		return a + b;
	}
};

int main() {
	// 当不需要返回值的时候"->类型"可以省略
	auto func1 = []()->void {
		cout << "hello world" << endl;
	};
	func1();
	// 对应的类写法
	Test1<> t1;
	t1();

	auto func2 = [](int a, int b)->int {
		return a + b;
	};
	func2(1, 2);
	Test2<int> t2;
	t2(1, 2);

	return 0;
}

捕获外部变量
[]:不捕获外部变量
[=]:以传值的方式捕获外部的所有变量
[&]:以传引用的方式捕获外部的所有变量
[this]:捕获外部的this指针
[=,&a]:以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获
[a, b]:以传值的方式捕获外部的变量a、b
[a, &b]:以传值的方式捕获外部的变量a、以传引用的方式捕获外部的变量b

#include <iostream>

using namespace std;

template<typename T = void>
class Test1 {
public:
	Test1() {

	}
	// 对于lambda表达式而言,重载的operator()默认为常方法
	void operator()() const{
		cout << "hello world" << endl;
	}
};

template<typename T = int>
class Test2 {
public:
	Test2() {

	}
	T operator()(T a, T b) const{
		return a + b;
	}
};

template<typename T = int>
class Test3 {
public:
	// 通过构造函数的方式捕获外部变量
	Test3(T& a, T& b) :ma(a), mb(b){

	}
	void operator()() const {
		// 相当于是直接修改了引用变量的内存
		T tmp = ma;
		// 由于这里不是改变引用变量本身,即不是改变引用变量ma的引用对象
		// 这是改变引用对象的值,相当于不修改指针,修改指针指向的值,所以在const方法里可以这么使用
		// 如果ma是普通数据,而不是引用变量,则不可改变ma
		ma = mb;      
		mb = tmp;
	}

private:
	T& ma;
	T& mb;
};

int main() {
	// 当不需要返回值的时候"->类型"可以省略
	auto func1 = []()->void {
		cout << "hello world" << endl;
	};
	func1();
	// 对应的类写法
	Test1<> t1;
	t1();

	auto func2 = [](int a, int b)->int {
		return a + b;
	};
	func2(1, 2);
	Test2<int> t2;
	t2(1, 2);

	int a = 10;
	int b = 20;
	// 需要传引用才能修改值
	auto func3 = [&a, &b]() {
		int tmp = a;
		a = b;
		b = tmp;
	};
	func3();
	cout << a << " " << b << endl;
	Test3<int> t3(a, b);
	t3();
	cout << a << " " << b << endl;
	return 0;
}

lambda表达式应用于泛型算法

	// 降序排列
	sort(vec.begin(), vec.end(), [](int a, int b)->bool {
		return a > b;
	});
	// 找第一个小于70的数
	auto iter = find_if(vec.begin(), vec.end(), [](int a)->bool {
		return a < 70;
	});
	// 输出所有的偶数
	for_each(vec.begin(), vec.end(), [](int a)->void {
		if (a % 2 == 0) {
			cout << a << endl;
		}
	});

哈希表应用

	map<int, function<int(int, int)>> calc_map;
	calc_map[1] = [](int a, int b)->int {return a + b; };
	calc_map[2] = [](int a, int b)->int {return a - b; };
	calc_map[3] = [](int a, int b)->int {return a * b; };
	calc_map[3] = [](int a, int b)->int {return a / b; };

智能指针自定义删除器

	// FILE* fp = fopen("main.cpp", "w");
	unique_ptr<FILE, function<void(FILE*)>> ptr(
		fopen("main.cpp", "w"),
		[](FILE* fp) {fclose(fp);}
	);

传入函数对象使得容器元素按照指定方式排列

class Data {
public:
	Data(int a, int b):ma(a), mb(b) {

	}
	int ma;
	int mb;
};

int main() {
	using FUNC = function<bool(Data&, Data&)>;
	//  priority_queue(const _Pr& _Pred)
	// 构造函数里面给函数对象
	priority_queue<Data, vector<Data>, FUNC> q([](Data& d1, Data& d2)->bool {
		if (d1.ma != d2.ma) {
			return d1.ma > d2.ma;
		}
		else {
			return d1.mb > d2.mb;
		}
	});
	q.push(Data(1, 2));
	q.push(Data(2, 3));
	q.push(Data(2, 2));
	while (!q.empty()) {
		cout << q.top().ma << " " << q.top().mb << endl;
		q.pop();
	}
	return 0;
}

对于lambda表达式而言,默认是const方法,不能改变捕获的变量,如果需要修改,则需要按引用&传递,则可以修改。因为在lambda表达式函数体内,修改的不是改变引用变量本身,改变引用对象的值。相当于不修改指针指向,修改指针指向的值,所以在const方法里可以这么使用
在这里插入图片描述

int a = 1;

auto func = [&]() {
	a = 0;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值