C++11 中的 lambda 函数

c++ 标准库里有一个函数 sort(),可以对容器的元素进行排序。它的函数原型是(参考资料 [1]):

template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);

template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

其中第三个参数是一个比较函数,它接收两个容器中的两个元素,返回比较结果。如果没有第三个参数,默认按元素的升序排列。

例如,我们要将一个整型数组降序排列:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

static bool compare_int(int a, int b)
{
    return (a > b);
}

int main(void)
{
    vector<int> vec = {5, 3, 1, 4, 2};

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << " ";
    cout << endl;

    sort(vec.begin(), vec.end(), compare_int);

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}

这里使用了 c++11 中的新特性:使用关键字 auto 自动推断变量 i 的类型,替代了显式的“vector::const_iterator”的类型声明。由于新标准规定的 auto 语义与原来的不同,编译的时候要加上选项“-std=c++11”(这里使用的编译器是 g++ 4.8.1)。

为了让元素降序排列,我们需要定义一个函数 compare_int() 用于比较。由于 c++ 中函数不能嵌套定义的规定,compare_int() 只能在 main() 函数外层定义,这样会导致在 compare_int() 之后定义的函数都能看到 compare_int();可是 compare_int() 只在 sort() 的时候被使用,它应该作为一个局部变量定义在 main() 中。使用新标准的 lambda 函数,上面的程序可以改写为:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(void)
{
    vector<int> vec = {5, 3, 1, 4, 2};

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << " ";
    cout << endl;

    sort(vec.begin(), vec.end(),
         [](int a, int b) { return (a > b); });

    for (auto i = vec.begin(); i != vec.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}

新的 c++11 标准允许定义匿名函数(即函数式编程中的 lambda 函数),例如这里传递给 sort() 函数的第三个参数:

[](int a, int b) { return (a > b); }

定义了一个 lambda 函数,它接收两个整型参数 a 和 b,返回 a 和 b 的比较结果。一般来说,c++ 中的 lambda 函数的形式为(参考资料 [2]):

[ capture ] ( params ) mutable exception attribute -> ret { body }
[ capture ] ( params ) -> ret { body }
[ capture ] ( params ) { body }
[ capture ] { body }

前面的定义省略了返回值,编译器会推测返回值的类型是 decltype(a > b),即一个 bool 值。更严格的写法应该是:

[](int a, int b) -> bool { return (a > b); }

中括号里可以指定 lambda 函数中要使用的外层函数的参数和传递方法(传值还是传引用)。例如下面的代码对一个数组的所有元素求和:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(void)
{
    vector<int> vec = {5, 3, 1, 4, 2};
    int sum = 0;

    for_each(vec.begin(), vec.end(),
             [&sum](int i) { sum += i; });

    cout << "sum = " << sum << endl;

    return 0;
}

这里的 for_each() 会遍历容器,并把每个元素作为参数传给第三个参数 fn:

template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function fn);

代码中的第三个参数是一个 lambda 函数,中括号中指定了把外层的变量 sum 作为引用传递给 lambda 函数,这样就可以在 lambda 函数中修改 sum 的值。lambda 函数支持以下的捕获变量的语法(参考资料 [2, 3]):

  • []: 不捕获任何变量

使用 auto 的新语义,我们可以在函数中定义 lambda 函数并将它赋给一个变量,在重用代码的同时又限制了函数的作用域。例如经典的“helloworld”例子:

#include <iostream>
using namespace std;

int main(void)
{
    auto func = [] { cout << "Hello, world." << endl; };

    func();

    return 0;
}

参考资料

[1] std::sort
[2] Lambda functions
[3] C++11

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注