C++为了函数的灵活使用,提供了不少的语言特性以及库对函数使用的支持。 首先是语言特性的支持,其中包括函数指针和C++11中引入的lambda表达式,还有标准库对函数使用的支持,其中主要包括函数适配器,以及泛型的function,基本取代了函数指针的使用。
一、函数指针
int love(const string &s) { cout<<"I love "<<s<<endl; } int hate(const string &s) { cout<<"I hate "<<s<<endl; } void get_love(decltype(love) *fc,const string &s) ///decltype将函数类型未自动转化为函数指针,而函数参数不能为函数类型,因此需要加指针修饰符 { fc(s); } int (*fis)(const string&); fis = &love; ///可将love的地址赋给fis fis("Hello , GNU!"); auto lovep = love; ///lovep 是指向love的指针 get_love(lovep,"GCC"); get_love(hate,"Windows");
当函数指针遇上重载的时候,auto和decltype就不能用了,编译器无法知道程序猿到底要那个函数类型,但函数指针还可以,不过必须明确指出函数指针的类型,不存在重载的函数指针。
int love(const string &s) { cout<<"string "<<s<<endl; } int love(const string &s,unsigned ui) { cout<<"int "<<ui<<" \tstring "<<s<<endl; } int (*lovepx)(const string&) = love; int (*lovepy)(const string&,unsigned)=love; lovepx("Open Source"); lovepy("To C or not to C",2);
二、lambda表达式
除了函数指针以外,C++11还引入了lambda表达式,每定义一个lambda表达式,编译器或为其生成一个内部类,同时定义该类的一个对象。
其简单的使用:
vector<string> vs={"one","two","three","four","five","six","seven"}; sort(vs.begin(),vs.end(),[](const string &x,const string &y){ ///将字符串按照最后一个字母的大小排序 return *(x.crbegin()) < *(y.crbegin()); }); for_each(vs.cbegin(),vs.cend(),[](const string& s){ ///输出到标准输出 cout<<s<<endl; }); cout<<endl;
lambda表达式一般多用于在函数内部传递一些小的操作,这种情况下我们就不必定义一个函数,而直接使用lambda表达式,他的限制是,不能在函数之间共享,同时太复杂的操作也不建议使用lambda表达式。当其返回值不是太复杂,而且只有一个返回语句时,可以省略其返回值声明,由编译器根据return语句自动推导,需要返回值时,可以如下书写
vector<string> vs={"Follow","My","Heart","!","Run","Say"}; transform(vs.cbegin(),vs.cend(),vs.begin(),[](const string &s)->string { if(s.size() > 5) { return s.substr(0,5); } else { return s; } });
除此之外,lambda表达式还可以共享局部变量,并且可以按值共享和按引用共享,如:
map<unsigned,string> mp={ {0,"zero"},{1,"one"},{2,"two"}, {3,"three"},{4,"four"},{5,"five"} }; int a=3; ///按值共享 for_each(mp.cbegin(),mp.cend(),[a](const pair<unsigned,string> p) { if(p.first > a) { cout<<p.first<<"\t"<<p.second<<endl; } else { cout<<p.first<<"\t"<<p.second+"!"<<endl; } }); int sum=0; vector<int> vit = {1,2,3,4,5,6,7,8,9,0}; ///按引用共享 for_each(vit.cbegin(),vit.cend(),[&sum](int i) { sum += i; }); ///a按值共享,sum按引用共享 for_each(vit.cbegin(),vit.cend(),[a,&sum](int i) { if(i > a) { sum += i; } }); ///除了sum之外,所有的局部变量按值共享 for_each(vit.cbegin(),vit.cend(),[=,&sum](int i) { if(i < a) { sum += i; } }); ///除了a以外,所有的局部变量都按照引用共享 for_each(vit.cbegin(),vit.cend(),[&,a](int i) { if(i != a) { sum += i; } });
三、关于bind
C++11中引入了bind,将原来复杂的参数绑定同一为一个接口,变得简单了
例如:
///将不小于5的元素删除 vector<unsigned> vs={0,1,2,3,4,5,6,7,8,9,10}; unsigned flag=5; vs.erase(remove_if(vs.begin(),vs.end(), bind(less_equal<unsigned>(),std::placeholders::_1,flag)),vs.end()); for(const auto &item:vs) { cout<<item<<endl; }
其中std::placeholders::_n指的是将绑定后的函数的第n个参数传递到和bind适配器中参数出现顺序对应的绑定前函数所在位置的形参,像上面的,就是将bind后所得函数的第一个参数(由std::placeholders::_1标示),也即唯一一个参数传递到less_equal<int>的对应应顺序,即第一个参数(std::placesholders::_1是bind函数参数部分的第一个),而less_equal的第二个参数,则被绑定位flag(flag时bind函数参数部分的第二个)
利用这个特性,还可以做更有趣的事情:
///按照降序排序 vector<unsigned> vs={0,1,2,3,4,5,6,7,8,9,10}; sort(vs.begin(),vs.end(), bind(less_equal<unsigned>(),std::placeholders::_2,std::placeholders::_2)); for(const auto &item:vs) { cout<<item<<endl; }
bind函数还可以用来绑定成员函数
class Boy { public: void love(const string &s)const { cout<<"I love "<<s<<endl; } }; Boy by; string dear=" beauty girl"; auto fcx = bind(&Boy::love,by,std::placeholders::_1); auto fcy = bind(&Boy::love,std::placeholders::_1,dear); fcx(dear); fcy(by);
注意:bind不能用来绑定重载函数
四、关于function
function是C++11中引入的可对同类函数提供统一接口的适配器
例如:
class Boy { public: int love(const string &s)const { cout<<"I love "<<s<<endl; } int operator()(const string &s) { } }; int love(const string &s) { } Boy by; auto lambdaf = [](const string &){return 0;}; auto bindf = bind(&Boy::love,by,std::placeholders::_1); vector<function<int(const string&)>> vf={lambdaf,bindf,love,by}; vf[0]("Apple"); vf[1]("Google"); vf[2]("Facebook"); vf[3]("IBM");
五、关于mem_fn以及C++中成员函数作为参数传递
function<bool(const string&)> fcx = &string::empty; ///通过传递引用来调用 function<bool(const string*)> fcxx = &string::empty; ///通过传递指针来调用 ///既可以传递引用也可以传递指针来调用 auto fcy = mem_fn(&string::empty); auto fcz = bind(&string::empty,std::placeholders::_1); vector<string> vs = {"American","China","Japan","England","Russia"}; vs.erase(remove_if(vs.begin(),vs.end(),fcy),vs.end()); copy_if(vs.begin(),vs.end(),vs.begin(),fcz); find_if(vs.begin(),vs.end(),fcx);