




已阅读5页,还剩9页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
VC10和C+ 0x (1) - lambda表达式C PLUS PLUS 2011-01-14 14:15:00 阅读84 评论0 字号:大中小订阅 【本文大部分内容译自Visual C+ Team Blog】/vcblog/archive/2008/10/28/lambdas-auto-and-static-assert-c-0x-features-in-vc10-part-1.aspx尽管C+社区对C+ 0x很是追捧,但是各厂商对于新标准的支持并不热乎。盼星星盼月亮,微软作为Windows平台上最强势的C+编译器厂商也终于在Visual Studio 2010中开始支持C+ 0x的特性。Lambda表达式,auto 和静态断言(static_assert)Visual Studio 2010中的Visual 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 C+0x Working Draft: /JTC1/SC22/WG21/docs/papers/2008/n2798.pdflambdas使用过函数式编程语言(如lisp, F#)或一些动态语言(如Python,Javascript)的大侠对于lambda表达式一定不会陌生。在C+ 0x中,引入了lambda表达式来定义无名仿函数。下面是一个lambda表达式的简单例子:/ File: meow.cpp#include #include #include #include using namespace std;int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); for_each(v.begin(), v.end(), (int n) cout n ; ); cout cl /EHsc /nologo /W4 meow.cpp NUL & meow0 1 2 3 4 5 6 7 8 9for_each一行中,中括号称为lambda introducer, 它告诉编译器接下来的是一个lambda表达式;接下来(int n)是lambda表达式的参数声明;最后大括号里边就是“函数体”了。注意这里因为lambda表达式生成的是functor,所以“函数体”实际上是指这个functor的operator ()的调用部分。你也许会问:那么返回值呢?缺省情况下lambda表达式生成的functor调用返回类型为void。所以,可以理解为上边的代码会被编译器翻译成如下: #include #include #include #include using namespace std;struct LambdaFunctor void operator()(int n) const cout n ; ;int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); for_each(v.begin(), v.end(), LambdaFunctor(); cout endl; return 0;为了方便,以下会用lambda返回void的简短表述来代替冗长啰嗦的表述:lambda表达式生成一个functor类型,这个functor类型的函数调用操作符(operator())返回的类型是void.请大家一定记住:lambda表达式生成了类型,并构造该类型的实例。下面的例子中lambda表达式的“函数体”包含多条语句:#include #include #include #include using namespace std;int main() vector 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表达式缺省情况下返回void. 那么如果需要返回其他类型呢?答案是:lambda表达式的“函数体”中如果有一个return的表达式,例如 return expression; ,那么编译器将自动推演expression的类型作为返回类型。#include #include #include #include #include #include using namespace std; int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); deque d; transform(v.begin(), v.end(), front_inserter(d), (int n) return n * n * n; ); for_each(d.begin(), d.end(), (int n) cout n ; ); cout 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导入符中可以指定一个“捕获列表”(capture-list)。int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); int x = 0; int y = 0; cout x y; v.erase(remove_if(v.begin(), v.end(), x, y(int n) return x n & n y; ), v.end(); 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) const return m_a n & n m_b; private: int m_a; int m_b;int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); int x = 0; int y = 0; cout x y; v.erase(remove_if(v.begin(), v.end(), LambdaFunctor(x, y), v.end(); copy(v.begin(), v.end(), ostream_iterator(cout, ); cout endl;上面代码中很重要的一点信息是:lambda中捕获的局部变量是以“传值”的方式传给匿名函数对象的。在匿名函数对象中,保存有“捕获列表”中局部变量的拷贝。这一点使得匿名函数对象的生命周期能够长于main中的x,y局部变量。然而这样的传值方式带来几个限制:1. lambda中的这两个拷贝并不能被改变,因为缺省情况下函数对象的operator()是const; 2. 有的对象的拷贝操作开销很大或者不可能(例如如果上面代码中的x, y是数据库链接或者某个singleton) 3. 即使在lambda内部修改了m_a, m_b也不能够影响外边main函数中的x和y 既然有了“传值”,你一定猜到了还会有“传引用”。bingo! 你是对的。在讨论“传引用”之前,我们先来看看另一个比较有用的东西。假设你有一大堆的局部变量需要被lambda使用,那么你的“捕获列表”将会写的很长,这肯定不是件愉快的事情。好在C+委员会的老头们也想到了,C+ 0x中提供了一个省心的东西:如果捕获列表写成 =,表示lambda将捕获所有的局部变量,当然也是传值方式。这种方式姑且被称为“缺省捕获”(capture-default)。int main() vector v; for (int i = 0; i 10; +i) v.push_back(i); int x = 0; int y = 0; cout x y; / EVIL! v.erase(remove_if(v.begin(), v.end(), =(int n) return x n & n y; ), v.end(); for_each(v.begin(), 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 v; for (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 , 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 ; 因为我们实际上是取的x,y的引用而不是地址。int main() vector 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 ; ); 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: LambdaFunctor(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 v; for (int i = 0; i 10; +i) v.push_back(i); int x = 1; int y = 1; for_each(v.begin(), v.end(), LambdaFunctor(x, y); copy(v.begin(), v.end(), ostream_iterator(cout, ); cout endl; cout x , y endl;注意:当你使用lambda时,VC10编译器会为lambda的定义部分自动禁用C4512警告。当以传引用方式捕获局部变量时,lambda的函数对象在自己内部以引用方式保存main函数中的局部变量。当然因为使用的是局部对象的引用,使用lambda表达式时一定要注意不能够超出局部变量的生命周期。和上文提高的=类似,我们可以用&来以“传引用”的方式捕获所有的局部变量。到目前为止,局部变量的捕获方式要么是“值语义”要么是“引用语义”,那么可以混合这两种方式吗?可以!例如:a, b, c, &d, e, &f, g,其中变量d和f是按引用语义捕获,而a,b,c,e和g是按值语义捕获。另外很有用的一点是:你可以指定一个缺省捕获(capture-default),然后重载(override)某些局部变量的捕获方式。下边例子中=, &sum, &product告诉编译器用值语义方式捕获所有的局部变量,但是有两个例外 - sum和product是按引用语义来捕获。int main() vector 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 (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 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& v) const for_each(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 v; for (int i = 0; i 3; +i) v.push_back(i); 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(toys) void meow(const vector& 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 v; for (int i = 0; i m_toys。当然你也可以让编译器省省力气。显式地在捕获列表中使用 this-m_toys。lambda比较智能,你也可以隐式地捕获this指针。如下所示:class Kitty public: explicit Kitty(int toys) : m_toys(toys) void meow(const vector& v) const for_each(v.begin(), v.end(), =(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 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 v; int i = 0; generate_n(back_inserter(v), 10, & return i
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 难点解析-人教版八年级上册物理《声现象》综合测评试卷(含答案详解版)
- 井都中学中考试卷真题及答案
- 护航学生考试题及答案解析
- 重难点解析人教版八年级上册物理物态变化《汽化和液化》综合练习试卷(附答案详解)
- 考点攻克人教版九年级《电功率》综合测试试卷(解析版含答案)
- 2025江苏公考试卷真题及答案
- 2025护师主管考试真题及答案
- 成安一中考试试卷真题及答案
- 屋面防水工程施工考试题及答案
- 药品管理法gsp考试试卷及答案
- 自助终端机设备管理制度
- 写字楼公司门禁管理制度
- 孔压静力触探测试技术规程培训
- 实验室风险评估报告(金坛区疾控中心)
- 跌倒护理不良事件案例分析
- T/DZJN 20-2020家用和类似用途饮用水处理装置用炭棒和炭棒滤芯组件
- 人教版七年级上学期期中考试数学试卷及答案(共7套)
- 湘艺版九年级上册第四单元《世界民族之声(二)》音乐单元作业设计
- 器械性压疮的护理
- 中国老年患者术后谵妄防治专家共识
- 兄妹断绝协议书范文
评论
0/150
提交评论