修改 Boost Multi-Index 项的非索引字段的最佳方法:修改与可变
Best way to modify non-indexed field of Boost Multi-Index item: modify vs mutable
我遇到的情况类似于此线程中描述的情况:Getting around Boost Multi-Index container's constant elemets. In short, I have a boost multi index container 持有这样的结构:
struct Data {
Controller controller;
const int indexed_data;
}
我的索引数据永远不会改变,但控制器会改变。因此,我的第一种方法是在 modify 调用中完成我需要的所有工作:
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,但检测到索引命中并删除了正在修改的员工。
就像我说的,我没有对容器的密钥进行任何更改,但在发现这一点后我很担心。这是图书馆的错误吗?还有其他情况会导致索引的无效状态吗?除了手动 deleting/modifying "invalid" 条目之一之外,我也找不到将索引 "reprocess" 变为有效状态的方法。
除此之外(回到我自己的案例),正在使用 modify 在我的控制器上实际调用方法的最佳方法,或者我应该遵循 线程的建议前面提到 并声明我的控制器mutable
?后者对我来说看起来很危险,因为通过声明任何东西 mutable
很容易弄乱索引,但正如我刚才展示的那样,安全的做法已被证明毕竟不是那么安全。
你提出这个问题很了不起,因为 modify
的行为在 12 年多的时间里一直保持稳定,直到 Jon Kalb pointed my attention to a closely related problem in 2016.
简而言之,modify
希望用户提供的修饰符不会抛出,除非它不更改元素的键,在这种情况下,会出现未定义的行为,正如您所发现的那样。文档是这样说的(斜体是我的):
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly mod
), then the element pointed to by position
is erased.
这是错误还是设计缺陷值得商榷,但无论如何我最近更改了实现,因此 your use case will now result in the element being erased。此更新的行为将在即将发布的 Boost 1.66 版本(2017 年 12 月)中可用,届时文档将显示为:
Exception safety: Basic. If an exception is thrown by some user-provided operation (including mod
), then the element pointed to by position
is erased.
与此同时,您需要保护您的修改器免受修改后抛出的影响——对此感到抱歉。如果您迫切需要新的 modify
行为,您可以下载 Boost.MultiIndex 源代码并修补您的本地 Boost 安装。
我遇到的情况类似于此线程中描述的情况:Getting around Boost Multi-Index container's constant elemets. In short, I have a boost multi index container 持有这样的结构:
struct Data {
Controller controller;
const int indexed_data;
}
我的索引数据永远不会改变,但控制器会改变。因此,我的第一种方法是在 modify 调用中完成我需要的所有工作:
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,但检测到索引命中并删除了正在修改的员工。
就像我说的,我没有对容器的密钥进行任何更改,但在发现这一点后我很担心。这是图书馆的错误吗?还有其他情况会导致索引的无效状态吗?除了手动 deleting/modifying "invalid" 条目之一之外,我也找不到将索引 "reprocess" 变为有效状态的方法。
除此之外(回到我自己的案例),正在使用 modify 在我的控制器上实际调用方法的最佳方法,或者我应该遵循 线程的建议前面提到 并声明我的控制器mutable
?后者对我来说看起来很危险,因为通过声明任何东西 mutable
很容易弄乱索引,但正如我刚才展示的那样,安全的做法已被证明毕竟不是那么安全。
你提出这个问题很了不起,因为 modify
的行为在 12 年多的时间里一直保持稳定,直到 Jon Kalb pointed my attention to a closely related problem in 2016.
简而言之,modify
希望用户提供的修饰符不会抛出,除非它不更改元素的键,在这种情况下,会出现未定义的行为,正如您所发现的那样。文档是这样说的(斜体是我的):
Exception safety: Basic. If an exception is thrown by some user-provided operation (except possibly
mod
), then the element pointed to byposition
is erased.
这是错误还是设计缺陷值得商榷,但无论如何我最近更改了实现,因此 your use case will now result in the element being erased。此更新的行为将在即将发布的 Boost 1.66 版本(2017 年 12 月)中可用,届时文档将显示为:
Exception safety: Basic. If an exception is thrown by some user-provided operation (including
mod
), then the element pointed to byposition
is erased.
与此同时,您需要保护您的修改器免受修改后抛出的影响——对此感到抱歉。如果您迫切需要新的 modify
行为,您可以下载 Boost.MultiIndex 源代码并修补您的本地 Boost 安装。