您的位置:首页 > 编程语言 > C语言/C++

[C/C++11语法]_[初级]_[lamba 表达式介绍]

2016-03-21 10:06 393 查看

场景

lambda 表达式在很多语言里都有一席之地,因为它的原因,可以在函数里快速定义一个便携的函数,或者在函数参数里直接快速构造和传递.

它可以说是匿名函数对象,一般只适用于某个函数内,只做临时使用.

一般是需要在对某个数据临时特殊处理时使用,比如对某种参数类型进行限定的再次封装和行为约束.

参考

1. C# Lambda表达式及其优势

2. Lambda Expressions in C++

3. Exception Specifications (throw) (C++)

4. noexcept (C++)

说明

lambda 语法.

图1:



Capture Clause(捕抓条款)组合:

规则1:

[] : 空捕抓条款,表明 lambda body 不访问闭合范围(enclosing scope)的任何变量.

[&] : 以引用的方式访问闭合范围内的前面已声明变量.

[=] : 以值的方式访问闭合范围内的前面已声明的变量.

[this] : 访问类实例的this指针.

规则2

&,=,this 默认类型不能同时声明

相同类型的捕抓不能和默认类型同时声明,比如[&,&i] // 编译错误

不相同类型的非默认类型可以同时声明.比如[&i,j]

对同一个变量不能捕抓多次或者同时以不同捕抓方式声明. [&i,&i] [&i,i]

Parameter List(参数列表)

和捕抓列表不一样,lambda可以输入参数,一般情况下参数是为了和 C++ 函数转换才需要.

也可以使用 lambda 表达式作为参数.

在C++14里, 如果使用的是泛型参数,那么你可以使用 auto 声明.

auto y = [] (auto first, auto second)
{
return first + second;
};


Mutable Specification(Mutable关键字)

可以使用mutable来修改捕抓条款里声明的传值变量, 注意只是相当于声明了一个本地的mutable变量作为临时变量而已,并不会修改enclosing scope 变量范围的值. 看 例子1

Exception Specification(异常规范)

可以使用throw()来声明这个lambda 不抛出C++异常. 但是在C++11里这种使用方式已经被废弃.

Return Type(返回类型)

vs2010 必须声明返回类型.

gcc 可以不声明返回类型,但是body 里必须有能推导的 return 表达式类型.

其他

参考C++14 lambda Expression 的说明.

lambda 和 C++普通函数的转换.

根据C++14 lambda表达式条款6, lambda 可以转换为C++函数, 但是必须满足以下的转化条件,而且只能转换为闭包类型自带的特定类型的函数, 闭包类型自带了一个函数指针?.

The closure type for a non-generic lambda-expression with no lambda-capture has a public non-virtual non-

explicit const conversion function to pointer to function with C ++ language linkage (7.5) having the same

parameter and return types as the closure type’s function call operator.

– 转换前的 lambda 条件:

1. 非泛型.

2. 没有捕抓列表(即没有捕抓任何变量)

– 转换后的 函数

1. 同参数.

2. 相同返回类型.

3. 非虚拟

4. 非显式常量.(non-explicit const)

例子

例子1

lambda 在STL里的使用场景.

由于vs2010 并不支持lambda 到 C++ 函数的转换,所以并不能通过编译.

mutable 的作用.

vs2010

#include "stdafx.h"
#include <memory>
#include <Windows.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <regex>

class B
{
public:
B(int value):two("B")
{
one = value;
std::cout << "B" << std::endl;
}
~B(){two.clear(); std::cout << "~B" << std::endl;}
int one;
std::string two;
};

void TestSort()
{
std::cout << "TestSort" << std::endl;
// 2010也不支持快速枚举. for(B* b: bs)
// 创建10个对象

std::vector<B*> bs(10);
int value = 0;
std::generate(bs.begin(),bs.end(),[&value]()->B*
{
B* b = new B(++value);
return b;
});

// 搜索奇数的对象
std::vector<B*> bs2;
std::for_each(bs.begin(),bs.end(),[&bs2](B* b)
{
if(b->one % 2)
{
bs2.push_back(b);
}
});

// 排序之前是升序.
std::cout << "Before Sort ==" << std::endl;
std::for_each(bs2.begin(),bs2.end(),[](B* b)
{
std::cout << b->one << std::endl;
});

// 降序排列
std::cout << "After Sort ==" << std::endl;
std::sort(bs2.begin(),bs2.end(),[](B* first,B* second)
{
return first->one > second->one;
});

std::for_each(bs2.begin(),bs2.end(),[](B* b)
{
std::cout << b->one << std::endl;
});
}

typedef void (*FUNC)();
void Foo(FUNC func)
{
func();
}

void TestLambdaAsync()
{
std::cout << "TestLambdaAsync ==" << std::endl;
//2010 不支持lambda转换为FUNC,它只能用于template里的实现;需要vs2012以上才支持.vs2010支持lambda到FUNC的转换.
//     这样就可以直接在 CreateThread里使用 lambda.
//g++ 4.8.1 可以.
// Foo([](){std::cout << "lambda" << std::endl;});
// 错误   2   error C2664: “Foo”: 不能将参数 1 从“`anonymous-namespace'::<lambda6>”转换为“FUNC”
}

void TestMutable()
{
std::cout << "TestMutable==========" << std::endl;
int m = 0;
int n = 0;

//去掉mutable会出现编译错误.Error:表达式必须是可以修改的左值.
// mutable 作用之一就是省略掉本地变量的定义.
// [&, n] (int a){ int n1 = n; m = ++n1 + a; }(4);

[&, n] (int a)mutable{m = ++n + a; }(4);
std::cout << m << std::endl << n << std::endl;
}

int main(int argc, char const *argv[])
{
TestSort();
TestMutable();
return 0;
}


输出:

B
B
B
B
B
B
B
B
B
Before Sort ==
1
3
5
7
9
After Sort ==
9
7
5
3
1
TestMutable==========
5
0


例子2

使用了lambda 作为 pthread 的回调函数.

多线程下使用 shared_ptr 的方法.

gcc 4.8.1

// function_lambda_expression.cpp
// compile with: /EHsc /W4
#include <Windows.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <string.h>
#include "pthread.h"

class A
{
public:
A()
{
std::cout << "A" << std::endl;
buf_ = (char*)malloc(6);
strcpy(buf_,"hello");
}
~A()
{
free(buf_);
buf_ = NULL;
std::cout << "~A" << std::endl;
}
char* buf_;
/* data */
};

// g++ 4.8.1 支持lambda函数到普通函数的转换,但是有条件,不支持capture(推理)
// 查看C++14规范第6条款关于lambda表达式和普通C++函数的转换关系.
// 传递共享指针,多线程共享变量例子.
void TestLambdaAsync(std::shared_ptr<A>& a1)
{
std::cout << "Begin a1.use_count: " << a1.use_count() << std::endl;
pthread_t t1;
std::shared_ptr<A>* a = new std::shared_ptr<A>(a1);
std::cout << "After a1.use_count: " << a1.use_count() << std::endl;

// 如果是C函数指针作为参数,那么lambda也不能捕抓任何变量,如[&a],不然会报错.
// error: cannot convert 'TestLambdaAsync()::__lambda0' to 'void* (*)(void*)' for argument '3' to 'int pthread_create(pthread_t*, pthread_attr_t_* const*, void* (*)(void*), void*)'},NULL);
pthread_create(&t1,NULL,[](void* data)->void*
{
std::shared_ptr<A>* a = reinterpret_cast<std::shared_ptr<A>*>(data);
std::cout << "pthread_create: " << (*a)->buf_ << std::endl;

delete a;
return NULL;
},a);
}

int main()
{
std::cout << "Start ==" << std::endl;
std::shared_ptr<A> a(new A());

for (int i = 0; i < 10; ++i)
{
TestLambdaAsync(a);
}

while(a.use_count() > 1)
{
std::cout << "Sleep" << std::endl;
Sleep(1);
}

std::cout << "Exit ==" << std::endl;
}


输出:

Start ==
A
Begin a1.use_count: 1
After a1.use_count: 2
Begin a1.use_count: 2
After a1.use_count: 3
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
pthread_create: hello
Begin a1.use_count: 2
After a1.use_count: 3
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 3
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
Begin a1.use_count: 3
After a1.use_count: 4
pthread_create: hello
pthread_create: hello
Begin a1.use_count: 2
After a1.use_count: 3
pthread_create: hello
Sleep
pthread_create: hello
Exit ==
~A
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: