修改Boost Multi-Index项目的非索引字段的最佳方法:modify vs mutable

时间:2017-10-22 09:53:54

标签: c++ boost c++17 boost-multi-index

我的情况类似于此主题中描述的情况:Getting around Boost Multi-Index container's constant elemets。简而言之,我有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,但会检测到索引上的命中并删除正在修改的员工。

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

除此之外(并回到我自己的情况),使用modify实际调用控制器上的方法的最佳方法,或者我应该遵循thread beforementioned的建议并声明我的控制器mutable?后者对我来说看起来很危险,因为人们可以通过声明任何mutable来轻松搞砸索引,但正如我刚才所说,安全的做法事实证明毕竟不是那么安全。

1 个答案:

答案 0 :(得分:2)

你提出这个问题是值得注意的,因为$folders = @('A', 'B') $source = 'source' $destination = 'destination' $filter = '*.bak' $folders | foreach { $source_path = [io.path]::combine($source, $_) $destination_path = [io.path]::combine($destination, $_) gci $source_path -File -Filter $filter | sort -Property LastWriteTime -Descending | Select -first 1 | copy -Destination $destination_path } 的行为在Jon Kalb pointed my attention to a closely related problem in 2016之前一直保持稳定12年以上。

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

  

例外安全:基本。如果某些用户提供的操作(可能modify除外)引发异常,则mod指向的元素将被删除。

这是一个错误还是一个设计缺陷是有争议的,但无论如何我最近改变了实现,以便your use case will now result in the element being erased。这个更新的行为将在即将发布的Boost 1.66版本(2017年12月)中提供,届时文档将为:

  

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

与此同时,你需要保护修改器免受修改后的抛出 - 对此有所了解。如果您迫切需要新的position行为,可以下载Boost.MultiIndex源代码并修补本地Boost安装。