(六)C++中的functor与lambda


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


lambda表达式是一种匿名的函数,需先了解C++中的functor是什么。

1.C++中的functor

要理解C++中的functor,可以先看一个例子。

class TestFunctor{
     public:
          TestFunctor();
          ~TestFunctor();
          void operator()(std::string s) {
          cout << s << endl;
          }
};

int main() {
     TestFunctor A;
     A("Hello Functor!");
     // Hello Functor!

     return 0;
}

如上面这段程序,在类TestFunctor中通过重载()运算符,可以使的TestFunctor类的实例像函数一样被调用,这正是functor的核心思想。functor中文可称之为仿函数,是任何像函数一样的函数。之所以引入functor是因为functor不是简单的普通函数,有着比operator()更多的特征,functor可以有状态,如类TestFunctor可以有成员,functor还区分于普通函数的函数签名。

1.1 functor是可参数化的函数

也就是说可以通过传入参数来定制functor,如下代码

class TestFunctor{
     public:
          TestFunctor();
          TestFunctor(int v) {_value = v;};
          ~TestFunctor();
          void operator()(std::string s) {
               cout << _value << " " << s << endl;
          }
     private:
          int _value;
};

int main() {
     TestFunctor A(10);
     A("Hello Functor!");
     // 10 Hello Functor!
     A("Hi Functor!");
     // 10 Hi Functor!
     TestFunctor A(20);
     A("Hello Functor!");
     // 20 Hello Functor!
     A("Hi Functor!");
     // 20 Hi Functor!

     return 0;
}

通过类A可以传入参数,构造不同的functor。到这里还看不出来这种做法的真正妙处。假如现在有个需求,要给数组的每个元素加上10, 可通过以下方式:

inline void add10(int x) {
     cout << x + 10 << " ";
}

int main() {
     std::vector<int> v = { 1, 2, 3, 4, 5 };
     for_each(v.begin(), v.end(), test::functor::add10);
     std::cout << endl; // 11, 12, 13, 14, 15
     return 0;
}

不过,上述的方式是通过硬编码实现的,换一个加的值就要重新写一个函数,十分不方便。当然,可以使用模板的方式来做,如下:

template<int n>
inline void add(int x) {
     cout << x + n << " ";

}

int main() {
     std::vector<int> v = { 1, 2, 3, 4, 5 };
     for_each(v.begin(), v.end(), test::functor::add<10>);
     std::cout << endl; // 11, 12, 13, 14, 15
     return 0;
}

不过,模板函数的方式是在编译时需要确定参数和类型,因此其必须时const类型,不能传参数进去,也就是像如下方式,程序运行时会报错

int n = 10;
for_each(v.begin(), v.end(), test::functor::add<n>);

这正是functor可以派上用场的时候:

class TestFunctor{
     public:
          TestFunctor();
          TestFunctor(int v) {_value = v;};
          ~TestFunctor();
          void operator()(std::string s) {
               cout << _value << " " << s << endl;
          }
     private:
          int _value;
};

int main() 
{
    int val = 2;
    v = { 1, 2, 3, 4, 5 };
    for_each(v.begin(), v.end(), TestFunctor(val));
    std::cout << endl;
    // 3, 4, 5, 6, 7
}

1.2 STL中的functor

  • 二元算术运算functor,如plus, minus, multiplies, divides, modulus

  • 二元关系运算functor,如equal_to, not_equal_to, greater, greater_equal, less, less_equal

  • 二元逻辑运算functor,如logical_and, logical_or

  • functional中的一元functor,如logical_not

// logical_not example
#include <iostream>     // std::cout, std::boolalpha
#include <functional>   // std::logical_not
#include <algorithm>    // std::transform

int main () {
  bool values[] = {true,false};
  bool result[2];
  std::transform (values, values+2, result, std::logical_not<bool>());
  std::cout << std::boolalpha << "Logical NOT:\n";
  for (int i=0; i<2; i++)
    std::cout << "NOT " << values[i] << " = " << result[i] << "\n";
  return 0;
}

1.3 参数绑定

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

using namespace std;

int main() {
     vector<int> s = { 1, 2, 3, 4, 5 };
     vector<int> v;
     std::transform(s.begin(), s.end(),    // source
               std::back_inserter(v),    // destination
               std::bind(std::multiplies<int>(), std::placeholders::_1, 100)  // C++11
               );
     for(auto &i : v) {
          cout << i << " ";
     }
     // 100 200 300 400 500
     cout << endl;
     return 0}

如上代码中,transform函数需要一个一元functor,而std::multiplies是二元算术运算functor,因此需要使用bind函数给参数绑定参数。知道了bind函数后,前面介绍的函数参数化问题可通过如下方式解决:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>  // std:: bind

void addNumber(int i, int number)
{
    std::cout << i + number << std::endl;
}

int main()
{
    std::vector v = { 1, 2, 3, 4, 5 };
    for_each(v.begin(), v.end(), 	
             std::bind(addNumber, 
             std::placeholders::_1, 10));  // 11 12 13 14 15
}

2.lambda表达式

了解了functor,再来了解lambda表达式就比较简单了,labmda是匿名的functor语法糖以使得functor的定义更为容易。

#include<iostream>
using namespace std;
int main() {
     auto plus = [data=1](const int value)
     {
          return value + data;
     };

     cout<< (plus(2) == 3) << endl;
}

值得注意的是capture initializer [data=1]C++14才支持的。lambda可以通过[]中传入参数来捕获变量以创建或初始化functor的成员变量。可以把lambda当成对象,[]传入的就是对象的成员变量。借助lambda,之前的例子可以写为:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>  // std:: bind
using namespace std; 
int main() {
     v = { 1, 2, 3, 4, 5 };
     int nu = 12;
     for_each(v.begin(), v.end(), [value=nu](int x){ cout << x + value << " "; });
     std::cout << endl;
     // 13 14 15 16 17 
     return 0;
}

默认的()重载带const,c++在函数后加const的意义是,定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。

int operator()(const int value) const
{
     return value + data;
}

因此无法修改成员值:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>  // std:: bind
using namespace std; 
int main() {
     v = { 1, 2, 3, 4, 5 };
     int nu = 12;
     for_each(v.begin(), v.end(), [value=nu](int x){ cout << x + value << " "; value++});
     std::cout << endl;
     // 13 14 15 16 17 
     return 0;
}

可在lambda上稍事修改:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>  // std:: bind
using namespace std; 
int main() {
     v = { 1, 2, 3, 4, 5 };
     int nu = 12;
     for_each(v.begin(), v.end(), [value=nu](int x) mutable { cout << x + value << " "; value++});
     std::cout << endl;
     // 13 14 15 16 17 
     return 0;
}

当不时使用capture variables时,可以将lambda当作函数的参数:

#include <iostream>

inline int plus(int x, int (*getValue)()) {
     return getValue() + x;
}

int main()
{
     auto getValue = [](){ return 1;};
     int res = plus(100, getValue);
     std::cout <<"get value: " << res << endl;
}


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


参考资料

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值