3.5. Boost.Lambda

匿名函数 - 又称为 lambda 函数 - 已经在多种编程语言中存在,但 C++ 除外。 不过在 Boost.Lambda 库的帮助下,现在在 C++ 应用中也可以使用它们了。

lambda 函数的目标是令源代码更为紧凑,从而也更容易理解。 以本章第一节中的代码例子为例。

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4.  
  5. void print(int i)
  6. {
  7. std::cout << i << std::endl;
  8. }
  9.  
  10. int main()
  11. {
  12. std::vector<int> v;
  13. v.push_back(1);
  14. v.push_back(3);
  15. v.push_back(2);
  16.  
  17. std::for_each(v.begin(), v.end(), print);
  18. }

这段程序接受容器 v 中的元素并使用 print() 函数将它们写出到标准输出流。 由于 print() 只是写出一个简单的 int,所以该函数的实现相当简单。 严格来说,它是如此地简单,以致于如果可以在 std::for_each() 算法里面直接定义它的话,会更为方便; 从而省去增加一个函数的需要。 另外一个好处是代码更为紧凑,使得算法与负责数据输出的函数不是局部性分离的。 Boost.Lambda 正好使之成为现实。

  1. #include <boost/lambda/lambda.hpp>
  2. #include <iostream>
  3. #include <vector>
  4. #include <algorithm>
  5.  
  6. int main()
  7. {
  8. std::vector<int> v;
  9. v.push_back(1);
  10. v.push_back(3);
  11. v.push_back(2);
  12.  
  13. std::for_each(v.begin(), v.end(), std::cout << boost::lambda::_1 << "\n");
  14. }

Boost.Lambda 提供了几个结构来定义匿名函数。 代码就被置于执行的地方,从而省去将它包装为一个函数再进行相应的函数调用的这些开销。 与原来的例子一样,这个程序将容器 v 的所有元素写出至标准输出流。

与 Boost.Bind 相类似,Boost.Lambda 也定义了三个占位符,名为 1_, 2 和 __3。 但与 Boost.Bind 不同的是,这些占位符是定义在单独的名字空间的。 因此,该例中的第一个占位符是通过 boost::lambda::_1 来引用的。 为了满足编译器的要求,必须包含相应的头文件 boost/lambda/lambda.hpp

虽然代码的位置位于 std::foreach() 的第三个参数处,看起来很怪异,但 Boost.Lambda 可以写出正常的 C++ 代码。 通过使用占位符,容器 _v 的元素可以通过 << 传给 std::cout 以将它们写出到标准输出流。

虽然 Boost.Lambda 非常强大,但也有一些缺点。 要在以上例子中插入换行的话,必须用 "\n" 来替代 std::endl 才能成功编译。 因为一元 std::endl 模板函数所要求的类型不同于 lambda 函数 std::cout << boost::lambda::_1 的函数,所以在此不能使用它。

下一个版本的 C++ 标准很可能会将 lambda 函数作为 C++ 语言本身的组成部分加入,从而消除对单独的库的需要。 但是在下一个版本到来并被不同的编译器厂商所采用可能还需要好几年。 在此之前,Boost.Lambda 被证明是一个完美的替代品,从以下例子可以看出,这个例子只将大于1的元素写出到标准输出流。

  1. #include <boost/lambda/lambda.hpp>
  2. #include <boost/lambda/if.hpp>
  3. #include <iostream>
  4. #include <vector>
  5. #include <algorithm>
  6.  
  7. int main()
  8. {
  9. std::vector<int> v;
  10. v.push_back(1);
  11. v.push_back(3);
  12. v.push_back(2);
  13.  
  14. std::for_each(v.begin(), v.end(),
  15. boost::lambda::if_then(boost::lambda::_1 > 1,
  16. std::cout << boost::lambda::_1 << "\n"));
  17. }

头文件 boost/lambda/if.hpp 定义了几个结构,允许在 lambda 函数内部使用 if 语句。 最基本的结构是 boost::lambda::if_then() 模板函数,它要求两个参数:第一个参数对条件求值 - 如果为真,则执行第二个参数。 如例中所示,每个参数本身都可以是 lambda 函数。

除了 boost::lambda::if_then(), Boost.Lambda 还提供了 boost::lambda::if_then_else()boost::lambda::if_then_else_return() 模板函数 - 它们都要求三个参数。 另外还提供了用于实现循环、转型操作符,甚至是 throw - 允许 lambda 函数抛出异常 - 的模板函数。

虽然可以用这些模板函数在 C++ 中构造出复杂的 lambda 函数,但是你必须要考虑其它方面,如可读性和可维护性。 因为别人需要学习并理解额外的函数,如用 boost::lambda::if_then() 来替代已知的 C++ 关键字 ifelse,lambda 函数的好处通常随着它的复杂性而降低。 多数情况下,更为合理的方法是用熟悉的 C++ 结构定义一个单独的函数。