在 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<void(int*, int)>
? std::function<void(int *A, int n)>
中的A
和n
的目的是什么?
你是对的!我只是出于懒惰而复制了签名。:)
【参考方案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-)copyA
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')?