首页 > 代码库 > C++函数的灵活使用

C++函数的灵活使用

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




C++函数的灵活使用