lambda表达式

\(一个lambda表达式可以表示一个可调用的代码单元,可以将其理解成一个未命名的内联函数.\)

\(一个lambda表达式具有一个返回类型,一个参数列表,一个函数体,与函数不同,它可能定义在函数内部.\)

\(形式如下:\)

[capture list](prarameter list) -> return type {function body}
capture list : lambda所在函数中定义局部变量的列表

\(通常可以忽略列表和返回类型,但必须包含捕获列表和函数体.\)

auto f = [] { return 42; };

\(此例定义了一个可调用对象f,不接受参数,返回42.\)

\(lambda的调用方式与普通函数的调用方式相同,都是使用调用运算符:\)

cout << f() << endl;

\(lambda忽略括号和参数列表等价于指定一个空参数列表,返回值类型可由return推断.\)

向lambda传递参数

\(于普通函数一样,调用实参初始化形参,但lambda不能有默认参数.\)

[](const string &a, const string &b) { return a.size() < b.size(); }

\(空捕获列表表面此lambda不使用函数中的任何局部变量.\)

\(可以使用lambda来调用函数:\)

stable_sort(words.begin(), words.end(), [](const string &a, const string &b)
            { return a.size() < b.size(); };
使用捕获列表

\(虽然一个lambda可以出现在一个函数中,使用其局部变量,但它只能使用那些明确指明的变量.\)

\(lambda通过将局部变量包含在其捕获列表中来指出将会使用这些变量.\)

\(如:\)

[sz](const string &a)
	{ return a.size() >= sz; };

\(一个lambda表达式只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量.\)

\(捕获列表只用于局部非static白能力,lambda可以直接使用局部static变量和它所在哈纳树之外声明的名字.\)

值捕获

\(变量的捕获可以是值或引用.\)

\(与参数不同,被捕获的变量的值是在创建时拷贝而不是在调用时拷贝.\)

void fcn1() {
    size_t v1 = 42;
    auto f = [v1]{ return v1; };
    v1 = 0;
    auto j = f() // j = 42, f 保存了创建时v1的拷贝
}
引用捕获

\(可以采用引用方式捕获变量:\)

void fcn2() {
    size_t v1 = 42;
    auto f2 = [&v1] { return v1; };
    v1 = 0;
    auto j = f() // j = 0; f2保存的v1的引用
}

\(如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存在的.\\lambda捕获的是局部变量,这些变量在函数结束时就不存在了.\)

隐式捕获

\(\&告诉编译器采用捕获引用的方式.\\=表示采用值捕获方式.\)

\(还可以混合使用隐式捕获和显示捕获:\)

// os隐式引用捕获,c显示值捕获.
for_each(word.begin(), word.end(), [&, c](const string &s) { os << s << c; } );
// os显示引用捕获,c隐式值捕获.
for_each(word.begin(), word.end(), [=, &os](const string &s) { os << s << c; });
可变lambda

\(想改变捕获变量的值,必须加上关键字mutable.\)

void fcn3() {
    size_t v1 = 42;
    auto f = [v1] () mutable { return ++v1; };
    v1 = 0;
    auto j = f() // j = 43
}

\(一个引用捕获的变量是否可以修改依赖于此引用是否是const.\)

void fcn4() {
    size_t v1 = 42;
    auto f2 = [&v1] { return ++v1; };
    v1 = 0;
    auto j = f2(); // j = 1 (++0)
}
指定lambda返回类型

\(如果需要为一个lambda定义返回类型时,必须使用尾置返回类型:\)

tansform(vi.begin(), vi.end(), bi.begin(),
        [] (int i) -> int
         { if (i < 0) return -1; else return i;} );
参数绑定

\(很多时候函数可以代替lambda,但是函数不能作为参数,因此需要一个转化.\)

bind函数

\(bind函数是一个函数适配器,它接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表.\)

\(调用bind的一般形式为:\)

auto newCallable = bind(callable, arg_list);
newCallable : 可调用对象
arg_list : 逗号分隔参数列表

\(使用bind,我们可以将原来基于lambda的函数调用:\)

auto wc = find_if(word.begin(), word.end(),
                 [sz] (const string &a));

\(替换为如下使用函数的版本:\)

auto wc = find_if(word.begin(), word.end(),
                 bind(check, _1, sz));
bind参数

\(我们可以用bind修正参数的值,也可以用bind绑定给可调用对象中的参数或重新安排其顺序.\)

// g是一个有两个参数的可调用对象
auto g = bind(f, a, b, _2, _c, _1);

\(生成一个新的可调用对象,有两个参数,分别用占位符\_2和\_1表示.\\这个新的可调用对象将它自已的参数作为第三个和第五个参数传递给f.\\f的第一个,第二个和第四个参数分别被绑定到给定的a,b,c上.\)

\(实际上,这个bind调用会将:\)

g(_1, _2)

\(映射为:\)

f(a, b, _2, c, _1)

\(即,对g的调用会调用f,用g的参数代替占位符,再加上绑定的参数a,b,c.\)

\(调用g(X,Y)会调用:\)

f(a, b, Y, c, X)
用bind重排参数顺序
sort(word.begin(), word.end(), isShorter);
sort(word.begin(), word.end(), bind(isShorter, _2, _1));

\(第一个调用,它会调用isShorter(A, B).\\第二个调用,它就像调用isShorter(B,A).\)

\(绑定引用参数\)

for_each(word.begin(), word.end(), bind(print, os,  _1, ' ')); // 错误 : 不能拷贝os

\(希望传递给bind一个对象又不拷贝它,就必须使用ref函数:\)

for_each(word.begin(), word.end(), bind(print, ref(os), _1, ' '));

\(ref函数返回一个对象,包含给定的引用,此对象是可以拷贝的,标注库还有一个cref,用来保存const引用的类.\)

posted @ 2021-03-24 18:13  phr2000  阅读(45)  评论(0编辑  收藏  举报