修改 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 安装。