std::function std::bind
我们知道在C里面有函数指针这么回事,我们用函数指针的目的就是将仿函数作为参数,传递给另外一个函数,并供他调用。但是显然,函数指针那种写法还是相当恶心的,比如:
代码语言:javascript复制#include<iostream>
int callback(int x){
std::cout<<x<<std::endl;
}
int func(int(*f)(int)){
f(3);
}
int main(){
func(callback);
}
而C 11里提供了std::function这个工具来封装函数指针,上面的写法就可以变成下面的:
代码语言:javascript复制#include<iostream>
#include<functional>
int callback(int x){
std::cout<<x<<std::endl;
}
int func(std::function<int(int)>f){
f(3);
}
int main(){
func(callback);
}
用函数模板来体现作为参数的函数类型,看起来更优雅方便。
但是有时候,我们可能希望这个function能够绑定一些参数,那么就需要用到std::bind这个工具来封装旧的function,并返回新的function:
代码语言:javascript复制#include<iostream>
#include<functional>
int callback(int x1,int x2){
...
}
int main(){
std::function<int(int)> binded=std::bind(callback,10,std::placeholders::_1);
binded(3);
}
上面的意思是将int(int,int)这个函数封装成了int(10,int)这个函数并起名为binded,std::placeholders::_1表示的就是在调用时传入的第一个参数(这里是3)。非常好理解。
使用std::function和std::bind可以非常装逼的组合多个函数,非常具有模块化的思想,比如下面的函数:
代码语言:javascript复制#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
int main(){
int low=1;
int high=10;
std::function<bool(int)> judge=std::bind(
std::logical_and<bool>(),
std::bind(std::greater_equal<int>(),std::placeholders::_1,low),
std::bind(std::greater_equal<int>(),high,std::placeholders::_1)
);
std::vector<int>v={1,2,9,10,11};
int count = std::count_if(v.begin(),v.end(),judge);
std::cout<<count<<std::endl;
}
其实就是实现了统计[low,high]之间的数字个数的功能。。。
lambda表达式
lambda表达式我认为说白了就是一种匿名函数的简写形式,是一种仿函数的语法糖,可以看成是一个std::function对象,没什么稀奇的。
基本写法 基本写法如下:
代码语言:javascript复制[capture] (params) opt -> ret {body;};
比如下面的函数fun1、fun2就是等价的:
代码语言:javascript复制int callback(int x,int y){
return x y;
}
int main(){
std::function<int(int,int)> fun1=callback;
std::function<int(int,int)> fun2=[](int x,int y)->int{return x y;};
}
捕获外部变量
为了保证良好的封装性,lambda并不能随意访问非参数外的其他变量,这些变量的访问权限交由[]
来控制,具体用法如下:
[]
不捕获任何变量[&]
按引用捕获所有变量[=]
按值捕获所有变量[=,&foo]
按值捕获所有变量,并按引用捕获foo变量[foo]
按值捕获foo变量,不捕获其他变量[this]
捕获当前类中的this指针
比如下面的例子就是按值捕获了外部变量x,如果使用[]
,那么编译会报错。
#include<iostream>
int main(){
int x=10;
auto f=[=](){std::cout<<x;};
f();
}
需要注意的是,捕获的过程是在函数定义的时候就发生的,在捕获时就会复制一个新的变量,因此我们注意到下面的例子:
代码语言:javascript复制#include<iostream>
int main(){
int x=10;
auto f=[x](){std::cout<<x;};
x=9;
f(); //the output is 10
}
如果我们需要即时的捕获外部变量,我们就需要按引用捕获。
mutable选项 由于C 的规定,lambda表达式的operator()是const的,也就是说我们按值捕获的值默认是const的,因此我们对按值捕获的值是无法修改的,这就很蛋疼了?这时候就用到了mutable选项来取消const的限制了:
代码语言:javascript复制#include<iostream>
int main(){
int x=10;
auto f1=[x]()mutable{x ;};//编译正确
auto f2=[x](){x ;};//编译错误
}
简单应用 有了lambda函数,很多事情就变的简单了,比如之前的统计一定范围内数字个数的程序就可以修改成下面这样:
代码语言:javascript复制#include<iostream>
#include<vector>
#include<algorithm>
int main(){
int low=1;
int high=10;
std::vector<int>v={1,2,9,10,11};
int count = std::count_if(v.begin(),v.end(),[low,high](int x){return x>=low&&x<=high;});
std::cout<<count<<std::endl;
}
tuple元组
没想到C 里面竟然也有元组,元组这个东西其实就是一个简化的结构体,方便我们将不同的数据进行打包,跟python中的用法类似: 创建元组
代码语言:javascript复制#include<iostream>
#include<tuple>
#include<string>
int main(){
std::tuple<std::string,int> t1("df",324);
auto t2=std::make_tuple(3432,"dsf");
}
两种方法,一种是构造函数,一种是make_tuple函数。
提取元素
代码语言:javascript复制#include<iostream>
#include<tuple>
#include<string>
int main(){
std::tuple<std::string,int> t1("df",324);
std::cout<<std::get<1>(t1)<<std::endl;
int d;
std::tie(std::ignore,d)=t1;
std::cout<<d<<std::endl;
}
两种方法,一种是get函数,可以指定提取的元素位置,另一种是std::tie方法,可以一次提取所有的元素也可以提取某一个元素,其他位置用std::ignore占位。 如果忘记了tuple的大小,我们可以使用tuple_size来获得tuple的大小:
代码语言:javascript复制#include<iostream>
#include<tuple>
int main(){
std::tuple<int,int> t(23,43);
std::cout<<std::tuple_size<decltype(t)>::value;
}
元组连接
代码语言:javascript复制#include<iostream>
#include<tuple>
#include<string>
int main(){
std::tuple<std::string,int> t1("df",324);
std::tuple<int,int> t2(23,43);
auto t3=std::tuple_cat(t1,t2);
}
通过tuple_cat来连接两个tuple。
参考资料
深入应用c 11 C FAQ cppreference--tuple