c++ - C++ lambda 按值捕获语义和允许的优化
问题描述
当函子仅使用隐式捕获对象的某些数据成员时,编译器允许从按值默认捕获中省略什么?例如,
struct A {
// some members we care about:
char x;
int y;
// some huge amount of state we do not:
std::array<bool, 200000> z;
int foo() const { return y + 1 }
};
void bar() {
A a;
// must the entirety of a be copy captured, or is the compiler allowed to pick/prune?
auto l1 = [=](){ std::cout << a.x << ", " << a.y << std::endl; };
// ...
}
同样,何时允许早期评估忽略更广泛的捕获?
void baz(int i) {
A a2;
a2.y = i;
// capture fundamentally only needs 1 int, not all of an A instance.
auto l2 = [=](){ std::cout << a.foo() << std::endl; }
}
至少在某些情况下,对元素进行部分和完整的复制捕获应该没有超出 lambda 大小的可见外部影响,但我不知道在规范中的何处寻找允许优化的答案。
解决方案
我认为,原则上,编译器将被允许以一种仅捕获 as-if 规则下已使用成员的副本的方式对此进行优化。[expr.prim.lambda] §2的相关部分:
[…] 一个实现可以定义不同于下面描述的闭包类型,前提是这不会改变程序的可观察行为,除非改变:
- 闭包类型的大小和/或对齐方式,
- 闭包类型是否可以轻松复制,或者
- 闭包类型是否是标准布局类。
然而,在对闭包类型的快速测试检查中sizeof()
,似乎没有一个主要的编译器(clang、gcc、msvc)以这种方式优化闭包类型本身。
但是,应该注意的是,只有当您实际将从 lambda 表达式获得的对象存储在某个地方(例如,在 中std::function
)时,这才真正成为一个问题。通常情况下,lambda 表达式的结果将简单地用作某个函数模板的参数,然后被丢弃。在这种情况下,所有内容最终都被内联,优化器应该(并且在我的测试中这样做)只是丢弃生成的代码,用于复制从未引用的数据......</p>
推荐阅读
- javascript - 在高图中忽略仪表的最大值
- python - Python 应用程序的版本冲突,但不是交互式开发
- amazon-web-services - 如何使用 AWS CDK 解决 AWS 资源之间的循环依赖
- docker - nginx:在 /etc/nginx/conf.d/default.conf 的上游“api:5000”中找不到 [emerg] 主机
- python - 在这种情况下如何打破循环?
- android - 如果我通过 updateChildren() 方法将超过 10 个以上的项目更新到 firebase,我的应用程序 UI 被冻结或没有响应
- flutter - BoxConstraints 强制无限宽度 - 如何避免设置硬宽度或高度?
- amazon-web-services - 如何使用 Java sdk describe instances api 获取给定操作系统的 AWS EC2 AMI 映像 ID
- android - 如何使用 Kotlin 在 DatePickerDialog 中调用 OnDateChangedListener
- python - Python通过特定条件使用熊猫从数据框中获取价值且没有重复