在 lambda 中捕获 std::array

Posted

技术标签:

【中文标题】在 lambda 中捕获 std::array【英文标题】:Capture std::array in lambda 【发布时间】: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的目的是什么? 你是对的!我只是出于懒惰而复制了签名。:) 【参考方案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 
    ...

【讨论】:

以上是关于在 lambda 中捕获 std::array的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 lambda 中捕获这个引用('&this')?

在 lambda 中,引用的按值捕获是不是会复制底层对象?

在三元运算符中初始化捕获 lambda

是否可以提取 lambda 的捕获列表?

移动 lambda:一旦你移动捕获了只移动类型,如何使用 lambda? [复制]

如何在标准函数 lambda c++ 11 中正确捕获参数包