Skip to content

C++语言导学(6): lambda表达式

引言

lambda表达式是一个可执行的代码单元,可以看作是一个匿名内联(inline)函数,下面以匿名函数代指lambda表达式。

定义语法:

[capture list] (parameter list) -> return type { function body }

匿名函数的函数体一般操作比较少,而且匿名函数一般不会反复在多处使用,常是一次性的。

参数列表

比如:

auto f = [] { return 42; }

auto f = []() { return 42; } // 等效

这个函数没有指定参数就是没有参数,也没有指定返回值类型,则根据执行语句块里面返回的类型作为匿名函数的返回类型。

比如:

auto f = [](const string& a, cosnt string&b) { return a.size() + b.size(); }

lambda表达式定义的函数参数没有默认值,意味着每次必须提供和定义一样多的实参。

捕获列表

比如:

int sz1 = 1024;
int sz2 = 2048;

auto f = [sz1,sz2](const string& a) { return a.size() >= sz1 && a.size() <= sz2; }
auto wc = find_if(words.begin(), words.end(), [sz](const string &a) { return a.size() >= sz; });

这里的sz是本地变量,如果要让匿名函数可以使用本地变量,则需要在中括号(捕获列表)里面提供。

比如:

for_each(wc, words.end(), [](const string& s) { cout << s << " "; };

匿名函数可以使用cout,虽然cout并没有写到捕获列表中。

因为匿名函数定义时的中括号捕获列表里面只用于写本地非静态变量,如果是所在函数之外的则可以直接使用。

局部变量进入匿名函数使用传值或引用,比如:

void fnc1()
{
    size_t v1 = 42;    // local variable
    auto f = [v1] { return v1; }; // copy v1 into f

    v1 = 0;
    auto j = f(); // j=42, not 0
}

void func2()
{
    size v1 = 42; // local variable
    auto f2 = [&v1] { return v1; };

    v1 = 0;
    auto j = f2(); // j=0, not 42
}

当使用引用传递时,要注意局部变量离开作用域就销毁了,因此要注意匿名函数执行时所捕获的变量是否还有效。

传递变量还可以使用简写方式:

auto f = [] { return v1; }; // 不捕获
auto f = [=] { return v1; }; // 自动以传值形式捕获变量
auto f = [&] { return v1; }; // 自动以引用形式捕获变量

auto f = [&,c] { return v1 > c; }; // 除了变量c,其余自动引用传递捕获变量
auto f = [=,&c] { return v1 > c; }; // 除了c,其余自动传值传递捕获变量

捕获的变量是否可以在匿名函数内部被改变?

比如:

void fcn3()
{
    size_t v1 = 42;
    auto f1 = [v1] { return ++v1; }; // error: 传值形式不能改变捕获变量的值
    auto f2 = [v1]() mutable { return ++v1; }; // right: 使用了mutable标记
    v1 = 0;
    auto j = f2(); // j = 43
}

void fcn()
{
    size_t v1 = 42;
    const size_t v2 = 42;

    auto f1 = [&v1] { return ++v1; } // right
    auto f2 = [&v2] { return ++v2; } // error: v2 is const

    v1 = 0;
    auto j = f1(); // j = 1
}

一般是不在匿名函数内部改变捕获的变量的。

返回值

如果匿名函数体只有一条return语句,那么最终匿名函数的返回类型由这个return语句决定:

transform(vi.begin(), vi.end(), vi.begin(), 
                                [](int i){ return i < 0 ? -1 : i; });

如果匿名函数体有多条return语句,那么最终匿名函数的返回列席是void:

transform(vi.begin(), vi.end(), vi.begin(), 
                                [](int i){ if(i < 0) return -i; else return i; });

如果匿名函数指定了返回类型,则使用此返回类型:

transform(vi.begin(), vi.end(), vi.begin(), 
                                [](int i) -> int 
                                { if(i < 0) return -i; else return i; });