




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、.VC10和C+ 0x (1) - lambda表达式C PLUS PLUS 2011-01-14 14:15:00 阅读84 评论0 字号:大中小 订阅 【本文大部分内容译自Visual C+ Team Blog】 尽管C+社区对C+ 0x很是追捧,但是各厂商对于新标准的支持并不热乎。盼星星盼月亮,微软作为Windows平台上最强势的C+编译器厂商也终于在Visual Studio 2010中开始支持C+ 0x的特性。 Lambda表达式,auto 和静态断言(static_assert)Visual Studio 2010中的Visua
2、l C+编译器,即VC10, 包含了4个C+ 0x的语言特性 - lambda表达式,auto,static_assert 和 rvalue reference (右值引用). 相关链接:· C+0x language feature status: /JTC1/SC22/WG21/docs/papers/2008/n2705.html· C+0x library feature status: /JTC1/SC22/WG21/docs/papers/2008/n2706.html·
3、 C+0x Working Draft: /JTC1/SC22/WG21/docs/papers/2008/n2798.pdf lambdas使用过函数式编程语言(如lisp, F#)或一些动态语言(如Python,Javascript)的大侠对于lambda表达式一定不会陌生。在C+ 0x中,引入了lambda表达式来定义无名仿函数。下面是一个lambda表达式的简单例子:/ File: meow.cpp#include <algorithm>#include <iostream>#include <ostream&
4、gt;#include <vector>using namespace std;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; return 0; C:Temp>cl /EHsc /nologo /W4 meow.cpp > NUL && m
5、eow0 1 2 3 4 5 6 7 8 9 for_each一行中,中括号称为lambda introducer, 它告诉编译器接下来的是一个lambda表达式;接下来(int n)是lambda表达式的参数声明;最后大括号里边就是“函数体”了。注意这里因为lambda表达式生成的是functor,所以“函数体”实际上是指这个functor的operator ()的调用部分。你也许会问:那么返回值呢?缺省情况下lambda表达式生成的functor调用返回类型为void。 所以,可以理解为上边的代码会被编译器翻译成如下: #include <algorithm>
6、;#include <iostream>#include <ostream>#include <vector>using namespace std;struct LambdaFunctor void operator()(int n) const cout << n << " " ;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), LambdaFun
7、ctor(); cout << endl; return 0;为了方便,以下会用"lambda返回void"的简短表述来代替冗长啰嗦的表述:lambda表达式生成一个functor类型,这个functor类型的函数调用操作符(operator())返回的类型是void.请大家一定记住:lambda表达式生成了类型,并构造该类型的实例。 下面的例子中lambda表达式的“函数体”包含多条语句:#include <algorithm>#include <iostream>#include <ostream>#includ
8、e <vector>using namespace std;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); for_each(v.begin(), v.end(), (int n) cout << n; if (n % 2 = 0) cout << " even " else cout << " odd " ); cout << endl; return 0;上文提到了lambda表达式
9、缺省情况下返回void. 那么如果需要返回其他类型呢?答案是:lambda表达式的“函数体”中如果有一个return的表达式,例如 return expression; ,那么编译器将自动推演expression的类型作为返回类型。#include <algorithm> #include <deque> #include <iostream> #include <iterator> #include <ostream> #include <vector> using namespace std; int main() &
10、#160; vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); deque<int> d; transform(v.begin(), v.end(), front_inserter(d), (int n) return n * n * n;
11、); for_each(d.begin(), d.end(), (int n) cout << n << " " ); cout << endl; 上例中返回值n * n * n很简单,类型推演是显而易见的。但是如果lambda表达式中有非常复杂的表达式时,编译器可以无法推演出其类型,或者是推演出现二义性,这时候你可以显式地指明返回值类型。如下所示: transform(v.begin(), v.end(), front_inserter(d), (in
12、t n) -> double if (n % 2 = 0) return n * n * n; else return n / 2.0; ); 黑体部分中有的“-> double”显式地指明了lambda表达式的返回类型是double. 以上例子中的lambda都是无状态的(stateless),不包含任何数据成员。很多时候我们需要lambda包含数据成员以保存状态,这一点可以通过“捕获”(capturing)局部变量来实现。lambda表达式的导入符(lambda-introducer)是空的,也就是“”,表明该lambda是一个无状态的。但是在lambda导
13、入符中可以指定一个“捕获列表”(capture-list)。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; v.erase(remove_if(v.begin(), v.end(), x, y(int n) return x < n && n < y; ), v.end(
14、); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; 上边的代码中的lambda使用了局部变量x和y,将值介于x和y之间的元素从集合中删除。程序运行示例如下 - Input: 4 70 1 2 3 4 7 8 9 上边的代码可以理解为:class LambdaFunctor public: LambdaFunctor(int a, int b) : m_a(a), m_b(b) bool operator()(int n) c
15、onst return m_a < n && n < m_b; private: int m_a; int m_b;int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y), v.end()
16、; copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "); cout << endl; 上面代码中很重要的一点信息是:lambda中捕获的局部变量是以“传值”的方式传给匿名函数对象的。在匿名函数对象中,保存有“捕获列表”中局部变量的拷贝。这一点使得匿名函数对象的生命周期能够长于main中的x,y局部变量。然而这样的传值方式带来几个限制:1. lambda中的这两个拷贝并不能被改变,因为缺省情况下函数对象的operator()是const; 2. 有的对象的拷贝操作开销很大或者不可
17、能(例如如果上面代码中的x, y是数据库链接或者某个singleton) 3. 即使在lambda内部修改了m_a, m_b也不能够影响外边main函数中的x和y 既然有了“传值”,你一定猜到了还会有“传引用”。bingo! 你是对的。在讨论“传引用”之前,我们先来看看另一个比较有用的东西。假设你有一大堆的局部变量需要被lambda使用,那么你的“捕获列表”将会写的很长,这肯定不是件愉快的事情。好在C+委员会的老头们也想到了,C+ 0x中提供了一个省心的东西:如果捕获列表写成 =,表示lambda将捕获所有的局部变量,当然也是传值方式。这种方式姑且被称为“缺省捕获”(capture
18、-default)。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 0; int y = 0; cout << "Input: " cin >> x >> y; / EVIL! v.erase(remove_if(v.begin(), v.end(), =(int n) return x < n && n < y; ), v.end(); for_each(v.begin()
19、, v.end(), (int n) cout << n << " " ); cout << endl; 当编译器在lambda的作用范围内看到局部变量x, y时,它会以传值的方式从main函数中将他们捕获。下面我们来看如何突破前面提到的3点限制。 第一点,修改lambda表达式中的局部变量拷贝(e.g. m_a, m_b) 缺省情况下,lambda的operator ()是const 修饰的,但是你可以使用mutable关键字改变这一点。 int main() vector<int> v; fo
20、r (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(v.begin(), v.end(), =(int& r) mutable const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; cout << x << ",
21、" << y << endl; 代码运行结果如下0 0 0 6 24 60 120 210 336 5041, 1 这里我们解决了第一个限制,但是却产生了一个新的限制4. lambda中对捕获变量的修改并不会影响到main函数中的局部变量,因为lambda捕获局部变量使用的是传值方式 下面该“传引用”的方式登场了,它能够有效地解决2,3,4三个限制。 传引用的语法为: lambda-introducer &x, &y 这里的捕获列表应该理解为:X& x, Y& y ; 因为我们
22、实际上是取的x,y的引用而不是地址。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(v.begin(), v.end(), &x, &y(int& r) const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << "
23、" ); cout << endl; cout << x << ", " << y << endl; 运行结果如下 - 0 0 0 6 24 60 120 210 336 5048, 9 上面代码会被编译器“翻译”成:#pragma warning(push)#pragma warning(disable: 4512) / assignment operator could not be generatedclass LambdaFunctor public: LambdaFuncto
24、r(int& a, int& b) : m_a(a), m_b(b) void operator()(int& r) const const int old = r; r *= m_a * m_b; m_a = m_b; m_b = old; private: int& m_a; int& m_b;#pragma warning(pop)int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(
25、v.begin(), v.end(), LambdaFunctor(x, y); copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "); cout << endl; cout << x << ", " << y << endl; 注意:当你使用lambda时,VC10编译器会为lambda的定义部分自动禁用C4512警告。当以传引用方式捕获局部变量时,lambda的函数对象在自己内部以引用方式保存main函数中的
26、局部变量。当然因为使用的是局部对象的引用,使用lambda表达式时一定要注意不能够超出局部变量的生命周期。 和上文提高的=类似,我们可以用&来以“传引用”的方式捕获所有的局部变量。 到目前为止,局部变量的捕获方式要么是“值语义”要么是“引用语义”,那么可以混合这两种方式吗?可以!例如:a, b, c, &d, e, &f, g,其中变量d和f是按引用语义捕获,而a,b,c,e和g是按值语义捕获。 另外很有用的一点是:你可以指定一个缺省捕获(capture-default),然后重载(override)某些局部变量的捕获方式。下边例子中=,
27、&sum, &product告诉编译器用值语义方式捕获所有的局部变量,但是有两个例外 - sum和product是按引用语义来捕获。 int main() vector<int> v; for (int i = 0; i < 10; +i) v.push_back(i); int sum = 0; int product = 1; int x = 1; int y = 1; for_each(v.begin(), v.end(), =, &sum, &product(int& r) mutable sum += r; if (
28、r != 0) product *= r; const int old = r; r *= x * y; x = y; y = old; ); for_each(v.begin(), v.end(), (int n) cout << n << " " ); cout << endl; cout << "sum: " << sum << ", product: " << product << endl; cout << "
29、;x: " << x << ", y: " << y << endl;运行结果如下 - 0 0 0 6 24 60 120 210 336 504sum: 45, product: 362880x: 1, y: 1 再来看看下边的代码 - 在lambda中使用类成员变量 class Kitty public: explicit Kitty(int toys) : m_toys(toys) void meow(const vector<int>& v) const for_eac
30、h(v.begin(), v.end(), m_toys(int n) cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<int> v; for (int i = 0; i < 3; +i) v.push_back(i);
31、 Kitty k(5); k.meow(v); 不幸的是,编译这段代码将产生这样的错误: error C3480: 'Kitty:m_toys': a lambda capture variable must be from an enclosing function scope为什么呢?lambda表达式能够让你不活局部变量,但是类的数据成员并不是局部变量。解决方案呢?别着急。lambda为捕获类的数据成员大开方便之门,你可以捕获this指针。class Kitty public: explicit Kitty(int toys) : m_toys(toy
32、s) void meow(const vector<int>& v) const for_each(v.begin(), v.end(), this(int n) cout << "If you gave me " << n << " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<
33、int> v; for (int i = 0; i < 3; +i) v.push_back(i); Kitty k(5); k.meow(v);运行结果 - If you gave me 0 toys, I would have 5 toys total.If you gave me 1 toys, I would have 6 toys total.If you gave me 2 toys, I would have 7 toys total. 当lambda表达式捕获“this”时,编译器看到m_toys后会在this所指向对象的范围内进行名字查找,m_toys被
34、隐式地推演为this->m_toys。当然你也可以让编译器省省力气。显式地在捕获列表中使用 this->m_toys。lambda比较智能,你也可以隐式地捕获this指针。如下所示:class Kitty public: explicit Kitty(int toys) : m_toys(toys) void meow(const vector<int>& v) const for_each(v.begin(), v.end(), =(int n) cout << "If you gave me " << n <
35、< " toys, I would have " << n + m_toys << " toys total." << endl; ); private: int m_toys;int main() vector<int> v; for (int i = 0; i < 3; +i) v.push_back(i); Kitty k(5); k.meow(v);运行结果:If you gave me 0 toys, I would have 5 toys total.If you gave me 1 toys, I would have 6 toys total.If you gave me 2 toys, I would have 7 toys total. 注意你也可以在上面代码中用 &,但是结果是一样的 - this指针永远是按值语义被传递(捕获)的。你也不能够使用 &this,呵呵。如果你的lambda表达式是没有参数的,那么lambda表达式的导入符后边的括号()也可以省掉。例如: int main() vector<int> v; int i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年江西烟草招聘考试真题及答案
- 考点解析人教版八年级上册物理光现象《平面镜成像》定向攻克试题(解析版)
- 2025年道路运输企业主要负责人和安全生产管理人员考试(安全生产管理人员)练习题及答案
- 2025年建筑结构设计与技术施工综合测评试题及答案
- 难点解析人教版八年级物理上册第5章透镜及其应用-5.5显微镜和望远镜专题训练试题(含详解)
- 2025年煤矿企业主要负责人安管能力考试考前模拟试题及答案
- 综合解析人教版八年级上册物理机械运动《运动的描述》综合练习练习题(含答案解析)
- 2025高中从句试题及答案解析
- 考点解析人教版八年级物理上册第5章透镜及其应用-凸透镜成像的规律专项训练练习题
- 综合解析人教版八年级物理《压强》单元测试练习题(含答案详解)
- 新闻记者职业资格《新闻采编实务》考试题库(含答案)
- 图解自然资源部《自然资源领域数据安全管理办法》
- 2024年四川省绵阳市中考英语试题卷(标准含答案)
- 股东之间股权转让合同协议书(2篇)
- PLC入门课程课件
- 港口液体危化品装卸管理人员理论考试题库(浓缩500题)
- 2024年深圳市龙华建设发展集团有限公司招聘笔试冲刺题(带答案解析)
- 药师竞聘正高述职报告
- 昇兴(安徽)包装有限公司年产 18 亿只铝制两片罐项目环境影响评价报告书
- 企业电气安全事故案例分析
- 2023学年完整公开课版液压方枕器
评论
0/150
提交评论