没有合适的资源?快使用搜索试试~ 我知道了~
首页C++ Lambda Story - From C++98 to C++20.pdf
现代C特性:Lambda表达式详解 - C 98至C 20.pdf
8 下载量 16 浏览量
更新于2023-11-24
评论
收藏 1017KB PDF 举报
《C Lambda Story - From C 98 to C 20》是一本适合所有喜欢了解现代C特性,尤其是Lambda表达式的C开发人员的书籍。该书于2019年3月25日推出第一版,并在2020年1月5日、4月17日、4月30日、6月19日进行了多次校对和更新。书中包括了C03、C11、C14、C17和C20等版本的Lambda技术列表、五大Lambda功能列表、代码证书等内容。读者如果发现任何错误,可通过提供的邮箱或反馈页面联系作者。此外,书中的代码遵循知识共享许可,可在在线编译器中进行语法高亮和限制。整体而言,该书内容丰富,适合C开发人员学习和参考。
资源详情
资源评论
资源推荐
C++ Lambda Story - From C++98 to C++20.md 1/1/2023
15 / 92
但是,你可以拷⻉ Lambda:
代码 2-4 拷⻉ Lambda
#include <type_traits>
int main() {
const auto firstLam = [](int x) noexcept {
return x * 2;
};
const auto secondLam = firstLam;
static_assert(std::is_same<decltype(firstLam), decltype(secondLam)>::value,
"must be the same");
}
如果拷⻉了⼀个 Lambda(实际上发⽣的是拷⻉构造),它的状态也会被拷⻉过来。
这⼀点对于捕获对象来说很重要。
因为,⼀个闭包类型会存储捕获的对象作为其成员字段。
所以,当进⾏ Lambda 拷⻉时,会拷⻉那些数据成员字段。
在 C++20 中,⽆状态 Lambda 会拥有默认的构造器和拷⻉赋值。
3. 调⽤操作符
我们传入 Lambda 中的参数部分,会被“转译”为相应闭包类型的调⽤操作符的参数。
默认情况下,在 C++11 中,他会被“转译”为⼀个常量内联成员函数。
例如
auto lam = [](double param) { /*do something*/ };
将被编译器展开为:
struct __anonymousLambda {
inline void operator()(double param) const { /*do something*/ }
};
重载
有⼀件事情值得提⼀下,那就是当你定义了⼀个 lambda 时,你不能创建它的任何重载形式来传入不同的参
数。
C++ Lambda Story - From C++98 to C++20.md 1/1/2023
16 / 92
// ⽆法编译
auto lam = [](double param) { /* do something */ };
auto lam = [](int param) { /* do something */ };
上⾯的代码将⽆法通过编译,因为编译器会将他们“转译”为⼀个仿函数,当然这就意味着⽆法重新定义⼀个相
同的变量。
但是,你可以在⼀个仿函数中定义两个调⽤操作符的重载形式,这是允许的:
struct MyFunctor {
inline void operator()(double param) { /* do something */ };
inline void operator()(int param) { /* do something */ };
};
MyFunctor 现在就可以同时接受 double 和 int 参数了。
如果你想在 Lambda 中实现相似的效果,那么你可以看看这部分内容 Lambda 继承
其他修饰符
我们在 Lambda 语法 ⼀节中简略介绍过这部分主题,但是你并不会被闭包类型调⽤操作符的默认声明所限制
到。
在 C++11 中,你可以添加 mutalbe 或者异常描述符。
如果可能的话,本书会使⽤⻓例⼦来⽤ const 标记闭包对象并且使 Lambda 为 noexcept。
你可以通过在参数声明后⾯那部分指定 mutable 或者 noexcept 来使⽤这些关键字。
auto myLambda = [](int a) mutable noexcept { /* do something */ };
编译器会展开为:
struct __anonymousLambda {
inline void operator()(int a) noexcept{ /* do something */ }
};
请注意,const 关键字此时会消失,并且调⽤操作符可以修改 Lambda 的成员变量了。
但是,成员变量呢?我们要如何在 Lambda 中声明成员变量?
请看下⼀个章节——关于「捕获」变量。
4. 捕获
C++ Lambda Story - From C++98 to C++20.md 1/1/2023
17 / 92
捕获⼦句-[] 操作符绝不仅仅只是 Lambda 的引入符号,同时它还兼顾捕获变量的列表的职能。
通过从 Lambda 表达式外部捕获变量,你可以在闭包类型中创建成员变量(非静态成员),然后,在 Lambda
函数体中,你就可以使⽤它了。
我们可以弄⼀个类似于 C++98/03 章节中 PrintFunctor 的内容,在这个类中,我们添加成员变量
std::string strText 并让他在构造函数中被初始化。
拥有⼀个成员变量可以让我们存储可调⽤对象的⼀些状态了。
⼀些有关捕获器的语法:
[&] - 引⽤捕获,⾃动捕获声明在捕获范围内的⽣命周期尚未结束的变量。
[=] - 值捕获(创建拷⻉),⾃动捕获声明在捕获范围内的⽣命周期尚未结束的变量。
[x, &y] - x 为值捕获,y 为显式引⽤捕获。
[args...] - 捕获⼀个模板参数包,全部都是值捕获
[&args...] - 捕获⼀个模板参数包,全部都是引⽤捕获
⼀些例⼦:
int x = 2, y = 3;
const auto l1 = []() {
return l1;
}; // 没有捕获
const auto l2 = [=]() {
return x;
}; // 值捕获(拷⻉)
const auto l3 = [&]() {
return y;
}; // 引⽤捕获
const auto l4 = [x]() {
return x;
}; // 仅值捕获x
const auto lx = [= x]() {
return x;
}; // 错误的语法,不需要=来对x显式进⾏拷⻉(值捕获)
const auto l5 = [&y]() {
return y;
}; // 仅引⽤捕获y
const auto l6 = [x, &y]() {
return x * y;
}; // 值捕获x,引⽤捕获y
const auto l7 = [=, &x]() {
return x + y;
}; // 全部都是值捕获,除了x是引⽤捕获
const auto l8 = [&, y]() {
return x - y;
}; // 全都是引⽤捕获,除了y是值捕获
为了理解在捕获变量的过程中发⽣了什么,让我们⼀起来思考下⾯这个例⼦:
C++ Lambda Story - From C++98 to C++20.md 1/1/2023
18 / 92
代码 2-5 捕获⼀个变量
std::string str{"Hello World"};
auto foo = [str]() {
std::cout << str << '\n';
};
foo();
上⾯这个 Lambda,str 被值捕获(构造了⼀个拷⻉)。
编译器将⾃动⽣成这样的仿函数:
代码 2-6 编译器可能⽣成的仿函数,单变量
class __unnamedLambda {
public:
inline /*constexpr */ void operator()() const {
std::operator<<(std::operator<<(std::cout, str), '\n');
}
private:
std::string str;
public:
__unnamedLambda(std::string _str) : str{_str} {}
};
如上述的展开代码,⼀个变量被传进构造函数中,在 Lambda 声明中被称为“就地”。
更准确的定义在 [expr.prim.lambda#21]:当解析 Lambda 表达式时,通过值捕获的实体将直接初始化在每个对
应⽣成的闭包对象中的非静态成员数据。
当然了,上述代码中的构造函数(__unnamedLambda)仅仅是⽤作演⽰和解释⽤途,编译器真正⽣成的内容会
与此有所差别,并且不会暴露给⽤户。
代码 2-7 引⽤捕获两个变量
int = 1, y = 1;
std::cout << x << " " << y << std::endl;
const auto foo = [&x, &y]() noexcept {
++x;
++y;
};
foo();
std::cout << x << " " << y << std::endl;
上述代码展开后,可能是:
C++ Lambda Story - From C++98 to C++20.md 1/1/2023
19 / 92
代码 2-8 编译器可能⽣成的仿函数,双变量,引⽤
class __unnamedLambda {
public:
inline /* constexpr */ void operator()() const noexcept {
++x;
++y;
}
private:
int& x;
int& y;
public:
__unnamedLambda(int& _x, int& _y) : x{_x}, y{_y} {}
};
由于我们是通过引⽤的⽅式捕获 x 和 y 的,所以闭包类型中的成员变量也是引⽤类型的。
请注意: 值捕获变量的值是在 Lambda 定义 时,⽽不是在 使⽤ 时。 但是引⽤捕获变量的内容是在
Lambda 使⽤ 时,⽽不是 定义 时。⼆者是有区别的。
虽然指定 [=] 或者 [&] 可能很⽅便,因为它会⾃动捕获仍在⽣命周期内的全部变量,但是,若能指明捕获的变
量是哪些,将会更加清晰明确。
这样编译器才能警告出哪些非预期的影响(参⻅ 全局变量 和 静态变量)。
当然,如果你想要了解更多更详细的内容,可以翻阅 Scott Meyers 所著的《Effective Modern C++》第 31 项
——“避免默认捕获模式”的内容。
请注意: C++ 闭包不会延⻓被捕获引⽤对象的剩余⽣命周期。请务必确保捕获对象在 Lambda 调⽤时
仍然“存活”。
mutable 关键字
通过闭包类型默认调⽤操作符获取的,都是带有 const 关键字限定的,你⽆法在 Lambda 表达式内部对他们做
出任何修改。
如果你希望进⾏修改的操作,那就需要在参数列表后添加 mutable 关键字。
它可以有效的去除闭包类型调⽤操作符中的 const 修饰符。举⼀个mutable 的简单例⼦:
int x =1;
auto foo =[x]() mutable { ++x; };
它会被展开为:
剩余91页未读,继续阅读
0x0007
- 粉丝: 1792
- 资源: 416
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- RTL8188FU-Linux-v5.7.4.2-36687.20200602.tar(20765).gz
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
- SPC统计方法基础知识.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0