一、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;
};