14

假设我有两个本地智能指针,foo并且bar.

shared_ptr<Foo> foo = ...
shared_ptr<Bar> bar = ...

这些智能指针是围绕资源的包装器,出于某种原因必须按顺序销毁这些资源foo,然后bar

现在我想创建一个使用fooand的 lambda bar,但在包含它们的范围内寿命更长。所以我会按价值捕获它们,如下所示:

auto lambda = [foo, bar]() { ... };

这会在函数对象内foo创建副本。bar当函数对象被破坏时,这些副本也将被破坏,但我关心这发生的顺序。所以我的问题是:

当一个 lambda 对象被破坏时,它的按值捕获被破坏的顺序是什么?我如何(希望)影响这个顺序?

4

4 回答 4

19

规范涵盖了这个......有点。从 5.1.2 第 14 段开始:

如果实体被隐式捕获并且捕获默认值为 =,或者如果使用不包含 & 的捕获显式捕获实体,则通过副本捕获实体。对于通过副本捕获的每个实体,在闭包类型中声明了一个未命名的非静态数据成员。这些成员的声明顺序是未指定的。

重点补充。由于未指定申报顺序,因此未指定构造顺序(因为构造顺序与声明顺序相同)。因此,破坏顺序是未指定的,因为破坏顺序与构造顺序相反。

简而言之,如果您需要关心声明顺序(以及与其相关的各种构造/销毁顺序),则不能使用 lambda。您需要制作自己的类型。

于 2012-09-20T20:57:02.067 回答
10

正如尼科尔所说,破坏的顺序是不确定的。

但是,您不必依赖 lambda 的破坏。您应该能够foo在 lambda 结束时简单地重置,从而确保它之前释放其资源bar。您还必须将 lambda 标记为mutable好像。这里唯一的缺点是你不能多次调用 lambda 并期望它工作。

auto lambda = [foo, bar]() mutable { ...; foo.reset(); };

如果您确实需要多次调用您的 lambda,那么您需要想出一些其他方法来控制释放的顺序。一种选择是使用具有已知数据成员顺序的中间结构,例如std::pair<>

auto p = std::make_pair(bar, foo);
auto lambda = [p]() { auto foo = p.second, bar = p.first; ... };
于 2012-09-20T21:33:27.163 回答
8

与其担心破坏的顺序是什么,不如解决这是一个问题的事实。请注意,您对两个对象都使用了共享指针,您可以通过在对象中添加一个共享指针来确保销毁顺序,该共享指针需要使另一个对象寿命更长。到那时,是否foo更早bar被销毁都无关紧要。如果顺序正确,共享指针的销毁将立即释放对象。如果顺序不正确,则附加的共享指针将保持对象处于活动状态,直到另一个对象消失。

于 2012-09-20T21:31:17.770 回答
5

根据我拥有的 C++11 文档(即免费赠品,稍早于批准 n3242),第 5.1.2 节,第 21 节,捕获按声明顺​​序构建并以相反的声明顺序销毁。但是,未指定申报顺序(第 14 段)。所以答案是,“以未指定的顺序”和“你不能影响它”(除了,我想,通过编写编译器)。

如果 bar 真的需要在 foo 之前被破坏,那么 bar 持有一个指向 foo 的共享指针(或类似的东西)是明智的。

于 2012-09-20T21:00:12.030 回答