【问题标题】:How to give a lambda an internal value that lasts as long as the lambda?如何给 lambda 一个与 lambda 一样长的内部值?
【发布时间】:2015-04-12 23:04:58
【问题描述】:

我想要一个变量,我可以在 lambda 中修改而不影响封闭范围。行为如下:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
{
  auto sum = 0;
  std::for_each(vec.begin(), vec.end(), [sum](int value) mutable
  {
    sum += value;
    std::cout << "Sum is up to: " << sum << '/n';
  });
}

但是,我希望能够做到这一点,而无需在 lambda 之外声明 sum 变量。像这样的:

std::vector vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

std::for_each(vec.begin(), vec.end(), [auto sum = 0](int value) mutable
{
  sum += value;
  std::cout << "Sum is up to: " << sum << '/n';
});

所以sum 仅在 lambda 内可见,而不是在封闭范围内。在 C++11/14 中可以吗?

【问题讨论】:

  • 删除 auto 并将 11 替换为 14 就可以了。
  • @KerrekSB 太棒了!谢谢:)

标签: c++ c++11 lambda c++14


【解决方案1】:

C++14 引入了通用 Lambda 捕获,让您可以随心所欲。

将根据 init 表达式的类型推断捕获,就像 auto 一样。

[sum = 0] (int value) mutable {
    // 'sum' has been deduced to 'int' and initialized to '0' here.
    /* ... */
}

【讨论】:

  • 有趣,这是我第一次看到有人用这个名字来引用这个功能。通常我会看到 init-capture,或“广义 lambda 捕获”。
  • @T.C.你是对的,它通常被那些名字所指。已编辑。
【解决方案2】:

如果您坚持使用 C++11(并且不能使用 C++14 的 lambda 捕获表达式,请参阅 @Snps 的答案),那么您可以在 lambda 中声明一个静态变量,就像在任何其他功能。示例如下:

#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    std::for_each(vec.begin(), vec.end(), [](int value)
    {
        static decltype(vec)::value_type sum{};
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    });
}

【讨论】:

  • 在我看来,“sum”的初始化每次运行只会发生一次。在此示例中,“main”无关紧要,但在一般情况下,这是一个问题。此外,静态会破坏可重入性(并且会限制优化)。见stackoverflow.com/questions/8391058/…
  • @greggo 不确定我理解你的意思 “在我看来,“sum”的初始化每次运行只会发生一次。” sum 仅被初始化在第一次调用时,就像任何静态变量一样。在常规功能中。感谢您的链接。
  • .. 所以如果你把它放在一个名为“sumvec”而不是“main”的函数中,第二次调用“sumvec”将不起作用,因为“sum”只初始化一次。跨度>
  • @greggo 好吧,这取决于一个人试图实现的目标。我同意在这方面使用捕获表达式更好,因为您可以“重用”相同的 lambda(即从闭包生成的对象)。
  • 我认为它不止于此。在任何一种情况下,每次调用“sumarr”都会构造一个新的 lambda 对象,但静态变量对所有实例都是通用的。从调用到调用的静态本地变量的用途非常有限,我不会称之为其中之一。
【解决方案3】:

至少在我看来,有更好的方法可以做到这一点。你在使用std::for_each,但是用它来模仿std::accumulate。后者是手头工作的明确选择:

#include <numeric>
#include <iostream>
#include <vector>

int main(){
    std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

    std::accumulate(vec.begin(), vec.end(), 0, [](int sum, int val) {
        sum += val;
        std::cout << "sum is up to: " << sum << "\n";
        return sum; });
}

我不确定这对你是否真的很重要,但这不需要 C++11 以外的任何功能。

【讨论】:

  • 该示例仅用于说明目的。 Accumulate 并不适合,而且名称在审查时会令人困惑。不过,请记住算法中还有其他功能。在尝试做某事之前,我并不总是检查。
  • @Kian:然而,您要求的是一个值,用于从 lambda 的一次调用到下一次累积(某种东西)......
  • 我可以看到“一个保留上一次调用的值并对其应用操作的变量”是一种累加器。例如,如果我想要一个索引,我会在每次迭代中累积并添加一个。但是, std::accumulate 返回累加器。如果您想使用索引,因为您需要从 lambda 中索引到某些其他结构,那么该名称会产生误导。积累是一个实现细节,我不在乎积累任何东西。 6 个月后阅读代码的人可能想知道您为什么调用累积并丢弃返回值。
【解决方案4】:

如果 C++14 不可用,您可以将所有内容包装在 lambda 中,该 lambda 将返回原始 lambda,并立即调用它。

std::for_each(vec.begin(), vec.end(), []()
{
    auto sum = 0;
    return [sum](int value) mutable
    {
        sum += value;
        std::cout << "Sum is up to: " << sum << '\n';
    };
}());

这样临时sum 的生命周期是最短的。

【讨论】:

    猜你喜欢
    • 2023-01-10
    • 1970-01-01
    • 2023-03-06
    • 2019-05-22
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    相关资源
    最近更新 更多