修改Boost多索引项的非索引字段的最佳方法:modify vs mutable

Best way to modify non-indexed field of Boost Multi-Index item: modify vs mutable

本文关键字:索引 方法 mutable modify 最佳 vs Boost 修改 字段      更新时间:2023-10-16

我遇到了与本线程中描述的情况类似的情况:绕过Boost多索引容器';s常数。简而言之,我有一个boost多索引容器,其中包含这样的结构:

struct Data {
Controller controller;
const int indexed_data;
}

我的索引数据永远不会更改,但控制器会更改。所以,我的第一个方法是在修改调用中完成我需要的所有操作:

container.modify(it, [](auto& data) {
data.controller.nonConstFunction();
});

显然,这对我来说是有效的,但我正在进行一些测试,以了解当lambda内部抛出异常时(在我的情况下会发生这种情况)该方法的行为,我对结果感到惊讶:

struct employee {
int id;
std::string name;
}
typedef multi_index_container<
employee,
indexed_by<
ordered_unique<BOOST_MULTI_INDEX_MEMBER(employee,int,id)>>
>
> employee_set;
employee_set es;    
es.insert(employee{0,"Joe"});
es.insert(employee{1,"Carl"});
es.insert(employee{2,"Robert"});
es.insert(employee{4,"John"});
try {
auto it = es.find(4); // John
es.modify(it, [](auto& employee) {
employee.id = 1; // Same ID of Carl, should be erased
throw std::runtime_error("test");
});
} catch (const std::runtime_error& err) {
// handle error...
}

在此之后,如果您打印容器中的内容,则会得到:

0 Joe
1 Carl
2 Robert
1 John

尽管没有抛出异常,只将员工的ID更改为已存在的ID,但会检测到对索引的命中,并删除正在修改的员工。

正如我所说,我不会对容器的密钥进行任何更改,但在发现这一点后,我很担心。这是图书馆的错误吗?是否存在其他可能导致索引无效状态的情况?除了手动删除/修改其中一个"无效"条目外,我也找不到将索引"重新处理"到有效状态的方法。

除此之外(回到我自己的情况),使用modify是在我的控制器上实际调用方法的最佳方法,还是应该遵循前面线程的建议并声明我的控制器mutable?后者对我来说看起来很危险,因为通过声明任何mutable都可以很容易地破坏索引,但正如我刚刚展示的那样,安全的方法最终被证明并不那么安全。

你提出这个问题很了不起,因为modify的行为已经稳定了12年多,直到Jon Kalb在2016年将我的注意力指向一个密切相关的问题。

简而言之,modify希望用户提供的修饰符不会抛出,除非它不更改元素的键,在这种情况下,会出现未定义的行为,正如您所发现的那样。文件是这么说的(我的斜体字):

异常安全:基本。如果某个用户提供的操作引发异常(可能不包括mod),则position指向的元素将被擦除。

这是一个bug还是一个设计缺陷是有争议的,但无论如何,我最近更改了实现,因此您的用例现在将导致元素被删除。此更新行为将在即将发布的Boost 1.66(2017年12月)中提供,届时文档将显示:

异常安全:基本。如果某个用户提供的操作(包括mod)引发异常,则position指向的元素将被擦除。

同时,你需要保护你的修改器不受修改后抛出的影响——很抱歉。如果您迫切需要新的modify行为,您可以下载Boost.MultiIndex源代码并修补您的本地Boost安装。