【问题标题】:Lambda expression with empty capture带有空捕获的 Lambda 表达式
【发布时间】:2017-03-30 13:40:22
【问题描述】:

我在使用 lambdas 时遇到了一个有趣的案例(至少对我而言),我想知道它是编译器错误还是标准功能允许的问题。

让我们切入正题。有示例代码:

const int controlValue = 5;
std::vector<int> vect{ 0, 1, 2, 3 };
const auto result = std::any_of(vect.begin(), vect.end(), [](const int& item)
{
    return item == controlValue;
});

请注意 controlValue 变量未被 lambda 表达式捕获。 此外,在 lambda 表达式的 cppreference 中,声明 [] - captures nothing

使用 VS2015 编译上述代码会出现错误,这并不奇怪:

error C3493: 'controlValue' cannot be implicitly captured because no default capture mode has been specified

但是,当将 MinGWgcc 4.8.2 一起使用时,相同的示例可以编译并运行。 一些在线编译器,包括 gcc 5.4.0clang 3.8.0 给出了类似的结果。

controlValue 丢失其const 时,所有经过测试的编译器都会给出所有预期的错误(变量未被捕获,这很好)。

在这种情况下,哪些编译器符合标准? 这是否意味着这里对 const 变量使用了一些优化或其他“技巧”?也许某些东西被隐含地捕获了? 谁能解释一下这里发生的情况?

编辑:

有人指出这个问题与Lambda capturing constexpr object 重复。虽然答案可能有些相关(指向 odr 用例),但问题是在通过 ref 捕获时发生错误。这里的主题完全不同,重点是根本不显式捕获变量(尽管在 lambda 主体中使用它)。

查看更多与 lambda 相关的问题后,如果有人感兴趣,我会指出 Using lambda captured constexpr value as an array dimension (与 @Barry 所述相同)暗示 VS2015 错误,并显示此处示例中的 controlValue 变量设置为 @987654333 @修复VS2015下的编译问题。

【问题讨论】:

  • 请注意,controlValue 是一个 constexpr 值。
  • 为什么这里被认为是constexpr?你能多解释一下或者给我一些文档吗?我相信constexpr 在某些情况下可以被认为是const,但反过来则不然
  • @Dusteh:在这种特殊情况下,您甚至可以使用const int controlValue = 5; std::array&lt;int, controlValue&gt; a;
  • 看看this answer

标签: c++ c++11 lambda language-lawyer


【解决方案1】:

这是一个 VS 错误。代码格式完美。

[expr.prim.lambda] 中的规则是:

如果 lambda 表达式或泛型 lambda 的函数调用运算符模板的实例化odr-uses (3.2) this 或从其到达范围内具有自动存储持续时间的变量,该实体应被 lambda 表达式捕获。

如果根据 [basic.def.odr],使用 odr 的变量:

名称显示为潜在求值表达式 ex 的变量 x 被 ex odr 使用 除非将左值到右值转换 (4.1) 应用于 x 产生一个常量表达式 (5.20)不会调用任何重要的 函数,如果 x 是一个对象,ex 是表达式 e 的潜在结果集合中的一个元素,其中 左值到右值转换 (4.1) 应用于 e,或者 e是一个弃值表达式(第 5 条)。

并且,来自 [expr.const]:

条件表达式 e 是一个核心常量表达式,除非 e 的求值遵循 抽象机(1.9),将评估以下表达式之一:[...] 左值到右值转换(4.1),除非它应用于整数或枚举类型的非易失性左值,它指的是完整的非-volatile const 对象,前面有初始化,用常量表达式初始化

在:

return item == controlValue;

controlValue 是一个整数类型的左值,它指的是一个用常量表达式初始化的完整的非易失性 const 对象。因此,当我们在涉及左值到右值转换的上下文中使用 controlValue 时,它不是 odr 使用的。因为它不是 odr-used,所以我们不需要捕获它。

当您将 controlValue 更改为 non-const 时,它不再是一个常量表达式,并且相等性检查 odr-使用它。由于它没有被捕获但被 odr 使用,因此 lambda 格式不正确。


请注意,标准中恰好出现了这样的示例:

void f(int, const int (&)[2] = {}) { }   // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
    const int x = 17;
    auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
    };

    // ...
}

【讨论】:

  • 一种解决方法,使用using controlValue_t = std::integral_constant&lt;int, controlValue &gt;;,然后在lambda 中使用controlValue_t{}controlValue_t::value,可能值得一提。当然,对于 MSVC,我不愿意在没有测试的情况下提出解决方法。 ;)
  • 谢谢,这看起来很有帮助。我只需要一些时间来消化它并阅读在这种情况下的 odr-uses :)
猜你喜欢
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 2015-11-23
  • 2015-08-20
  • 2023-03-12
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多