ein*_*ica 5 c++ lambda language-lawyer c++20
以下函数有效(从 C++20 开始):
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) l1;
}
Run Code Online (Sandbox Code Playgroud)
即使l1不捕获任何内容,据说它仍然可以“无捕获捕获” 的值b,因为它是 a const(它甚至不必是constexpr;但请参阅@StoryTeller 的评论)。
但如果我尝试在新的 lambda 中捕获更复杂的东西:
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) [](int c) { return l1(c) * c; };
}
Run Code Online (Sandbox Code Playgroud)
这无法编译。为什么?l1编译器从 lambda 内部调用应该没有问题;那么为什么b无捕获捕获可以,但l1不行呢?
这与 ODR 使用有关。
首先,来自[basic.def.odr]/10:
如果满足以下条件,则本地实体在某个范围内可使用 odr:
- 本地实体不是 *this,或者存在封闭类或非 lambda 函数参数范围,并且如果最里面的此类范围是函数参数范围,则它对应于非静态成员函数,并且
- 对于引入实体的点和范围(其中 *this 被认为是在最内层封闭类或非 lambda 函数定义范围内引入)之间的每个介入范围 ([basic.scope.scope]),可以是:
- 中间作用域是块作用域,或者
- 中间作用域是具有命名实体的简单捕获或具有捕获默认值的 lambda 表达式的函数参数作用域,并且 lambda 表达式的块作用域也是中间作用域。
如果本地实体在不可 odr 可用的范围内进行 odr 使用,则该程序格式错误。
所以在这个例子中,aodr 可用,但b不可用:
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) l1;
}
Run Code Online (Sandbox Code Playgroud)
类似地,在此示例中, 和a是codr 可用的,但 或b或 都不l1是。
void foo() {
constexpr const int b { 123 };
constexpr const auto l1 = [](int a) { return b * a; };
(void) [](int c) { return l1(c) * c; };
}
Run Code Online (Sandbox Code Playgroud)
但该规则不仅是“不可使用 ODR”,而且还“可使用 ODR”。其中哪些是 ODR 使用的?那是[basic.def.odr]/5:
如果表达式是表示变量的 id 表达式,则变量由表达式命名。名称显示为潜在计算表达式 E 的变量 x 被 E odr 使用,除非
- x 是可在常量表达式 ([expr.const]) 中使用的引用,或者
- x 是非引用类型的变量,可在常量表达式中使用,并且没有可变子对象,E 是非易失性限定的非类类型表达式的潜在结果集合中的一个元素,左值-应用到右值转换 ([conv.lval]),或者
- x 是非引用类型的变量,E 是未应用左值到右值转换的丢弃值表达式 ([expr.context]) 的潜在结果集的元素。
对于这种b * a情况,b是“可在常量表达式中使用的非引用类型的变量”,我们正在对它执行的操作是应用“左值到右值的转换”。这是该规则的第二个例外,因此不b使用odr,因此我们不存在 odr 使用但不可用 odr 的问题。
对于这种l1(c)情况,l1也是“可在常量表达式中使用的非引用类型的变量”...但我们不会对其进行左值到右值的转换。我们正在调用呼叫操作员。所以我们没有遇到异常,所以我们使用了 odr l1...但它不是 odr 可用的,这使得它的格式不正确。
这里的解决方案是捕获l1(使其可使用 odr)或使其static成为全局(使规则变得无关紧要,因为l1不再是本地实体)。
| 归档时间: |
|
| 查看次数: |
272 次 |
| 最近记录: |