【问题标题】:Capture std::array in lambda在 lambda 中捕获 std::array
【发布时间】:2020-02-18 14:06:06
【问题描述】:

我试图测试一些简单的 C 风格的排序函数。在驱动程序中,我写了这样的东西:

int main()
{
    std::array<int, 8> A = { 1, 0, 4, 5, 7, 2, 9, 3 };
    auto lambda = [&A](const std::function<void(int *A, int n)> &sorting_alg) {
        int n = A.size();
        sorting_alg(A.data(), n);
        std::cout << "=> ";
        print(A.data(), n);
        std::cout << std::endl;
    };

    auto do_bubble_sort = std::bind(lambda, bubble_sort);
    auto do_selection_sort  = std::bind(lambda, selection_sort);
    auto do_insertion_sort  = std::bind(lambda, insertion_sort);

    std::cout << "Bubble Sort :" << std::endl;
    do_bubble_sort();
    std::cout << "Selection Sort :" << std::endl;
    do_selection_sort();
    std::cout << "Insertion Sort :" << std::endl;
    do_insertion_sort();

    return 0;
}

我有绑定代码,我可以将 A 传递给它以复制,但它将我的 lambda 限制为 size=8,这是我想避免的。是否可以在不使用 std::vector 之类的东西的情况下实现这一目标? 一旦我将A 的捕获方法更改为值捕获,它就不再编译了。我想使用数组的副本来测试所有排序功能。为什么我不能按值捕获std::array?为什么尺寸推断适用于参考案例?

【问题讨论】:

  • OT:为什么不简单地std::function&lt;void(int*, int)&gt;std::function&lt;void(int *A, int n)&gt;中的An的目的是什么?
  • 你是对的!我只是出于懒惰而复制了签名。:)

标签: c++ c++11 lambda


【解决方案1】:

默认情况下,lambda 的 operator()const。这意味着您不能修改 lambda 的任何成员(它是捕获)。当您通过引用捕获时,这意味着您不能将引用更改为引用其他内容,但是由于您已经不能这样做,因此基本上没有任何意义。当您按值捕获时,它会生成该值const,这意味着您不能再像排序方法要求的那样进行修改(您需要data 来返回一个非常量指针)。

要解决这个问题,您可以使用 mutable 关键字使 lambda 的 operator() 不是 const。看起来像

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    int n = A.size();
    sorting_alg(A.data(), n);
    std::cout << "=> ";
    print(A.data(), n);
    std::cout << std::endl;
};

【讨论】:

  • (Re-)copy A in lambda 似乎更合适(因此其他排序代码将对原始数组进行排序,而不是排序数组。)
  • @Jarod42 我没有得到你的评论。按值捕获会生成副本。
  • 我的意思是删除 mutable 并像 Guillaume Racicot 的回答那样编写代码。对A(lambda)的副本进行排序。
  • @DanielLangr:但是您可能希望从相同的输入进行比较,而不是从排序的数组中进行比较。 (输入的副本是相同的,实际上比排序复杂度低)。
  • 另外,我可以在 lambda 中编写一些计时函数。
【解决方案2】:

您可以按值捕获,但您必须将 lambda 更改为可变的。

当您按值捕获时,默认情况下捕获是 const。您可以将其标记为可变或在本地制作副本:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) {
    auto ALocal = A;
    int n = ALocal.size();
    sorting_alg(ALocal.data(), n);
    std::cout << "=> ";
    print(ALocal.data(), n);
    std::cout << std::endl;
};

Live example

这具有使 lambda 可多次调用的优点。

您也可以通过引用捕获来做到这一点。

您也可以使 lambda 可变,但只能调用一次:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    int n = A.size();
    sorting_alg(A.data(), n);
    std::cout << "=> ";
    print(A.data(), n);
    std::cout << std::endl;
};

【讨论】:

  • “但只能调用一次” 可以多次调用,额外的时间只会对“排序”数组进行排序。
  • 您是指非递归调用还是 main() 本身?当然,bind 制作了副本,但我仍然可以让它运行不止一次?
  • @user2338150 如果你多次运行这样的可变 lambda,数据已经被排序,导致最初的问题再次出现。
  • 我明白了,如果我调用 do_bubble_sort() 两次,第二次迭代将在排序数组本身上运行。
【解决方案3】:

问题是lambda函数的operator()默认是const-qualified。这意味着通过 value 捕获的值在 lambda 主体中是 const-qualified。在您的代码中,您尝试使用从捕获的数组(即const)初始化的指向int 的非const 指针调用std::function

要解决此问题,您需要将 lambda 标记为 mutable,以便生成的 operator() 不再是 const-qualified:

auto lambda = [A](const std::function<void(int *A, int n)> &sorting_alg) mutable {
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-30
    • 2013-12-15
    • 2019-03-05
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 2012-05-04
    • 1970-01-01
    相关资源
    最近更新 更多