【C++学习】函数对象和Lambda表达式

作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/

在看《高效编程十八式》中的第一小节的时候介绍了函数对象方法和Lambda表达式,以便完成“如果需要对函数指针进行更加灵活的定制”。

假设任务判断奇偶数,为了达到数据封装,我们使用函数对象的方法:

#include 
#include 
#include 
using namespace std;
class Functor
{
public:
   // The constructor.
   explicit Functor(int& evenCount) 
      : _evenCount(evenCount)
   {
   }
   void operator()(int n)
   {
      cout << n;
      if (n % 2 == 0) 
      {
         cout << " is even " << endl;
         // Increment the counter.
         _evenCount++;
      }
      else 
      {
         cout << " is odd " << endl;
      }
   }
private:
   int& _evenCount; // the number of even variables in the vector
};
int main() 
{
   vector<int> v;
   for (int i = 0; i < 10; ++i) 
   {
      v.push_back(i);
   }
   int evenCount = 0;
   for_each(v.begin(), v.end(), Functor(evenCount));
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}

我们使用了一个类,这个类的作用就是判断奇偶并且进行计数,在for_each循环中,我们将这个Functor作为一个函数对象传入,此时调用Functor的构造函数,创建一个临时对象,当作用于vector每一个成员时,重载的()运算符发挥作用,表现的像个函数指针。显然这种方法在增加灵活性的同时却十分费力的写了一个如此长的类。

同样的任务我们使用Lambda表达式再写一遍:

#include 
#include 
#include 
using namespace std;
int main() 
{
 
   vector<int> v;
   for (int i = 0; i < 10; ++i) 
   {
      v.push_back(i);
   }
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) 
      {
         cout << " is even " << endl;
         // Increment the counter.
         evenCount++;
      }
      else 
      {
         cout << " is odd " << endl;
      }
   });
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}

在这里我们在for_each的传入函数部分使用lambda表达式写了一个类似于Java中的匿名函数的东西。整个程序显得自然顺畅。[]字段是lambda表达式的外部变量捕获规则字段,=表示按值访问,&表示按引用访问(这两个是定义默认捕获规则时使用,若指定单个变量的规则,则使用&在变量名前边表示引用,而按值则直接写变量名即可),若没有则不能访问任何外部变量。而()表示函数体的变量列表,这个与一般的函数没有什么区别。

1. Lambda表达式的语法:

以下边的这个例子来说明语法:

#include 
#include 
#include 
using namespace std;
// The number of elements in the vector.
const int elementCount = 9;
int main() 
{
   vector<int> v(elementCount, 1);
   int x = 1;
   int y = 1;
   generate_n(v.begin() + 2, elementCount - 2, [&, y]() mutable throw() -> int {
      
      // Generate current value.
      int n = x + y;
      // Update previous two values.
      x = y;
      y = n;
      return n;
   });
   // Print the contents of the vector.
   for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
   cout << endl;
   cout << x << " " << y << endl;
}

我们看到generate_n这个STL方法,在第三个参数位置上使用了Lambda表达式:

[&, y]() mutable throw() –> int{}

首先,如前所述,[ ]表示外部变量捕获规则字段,若是不带有变量名称的则定义的是整个表达式的默认的捕获规则,在这里是引用;若指明了某一个变量则表示这个变量是单独定义捕获规则的,这里的y就是单独定义为按值捕获。

第二个字段是( ),这是传入参数字段,类似于函数,只是它不能有默认参数、不能有变长参数列表,必须有变量名。这个字段是可选字段。

第三个字段是mutable字段,若设置为mutable则按值捕获的外部变量的副本在表达式内可以被修改,否则会出现read only的错误。这个字段可选。

第四个字段是throw字段,限定能够抛出哪些异常。这个字段可选。

第五个字段是->字段,表示的返回参数类型。若只有一个返回语句的话,也可以省略这个字段,由编译器自行判断,虽然并不建议这么做。当然,若没有返回值就直接省略这个字段。

注:g++的编译方式为g++-4.5 以上,加上--std=c++0x标识

最后我们再写一个例子:

#include 
#include 
#include 
int main()
{
   using namespace std;
   // Create a list of integers with a few initial elements.
   list<int> numbers;
   numbers.push_back(13);
   numbers.push_back(17);
   numbers.push_back(42);
   numbers.push_back(46);
   numbers.push_back(99);
   // Use the find_if function and a lambda expression to find the 
   // first even number in the list.
   const list<int>::const_iterator result =
      find_if(numbers.begin(), numbers.end(),
         [](int n) { return (n % 2) == 0; });
   // Print the result.
   if (result != numbers.end())
   {
       cout << "The first even number in the list is " 
            << (*result) 
            << "." 
            << endl;
   }
   else
   {
       cout << "The list contains no even numbers." 
            << endl;
   }
}

参考文献:

作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/

posted @ 2012-12-10 20:23  gnuhpc  阅读(1093)  评论(1编辑  收藏  举报