为什么不能将boost :: multi_index replace`用于指针类型?

时间:2014-04-01 05:47:46

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

简答:用户modify代替接受答案中的详细信息以及this answer

我正在尝试使用持有指针类型的boost::multi_index_container。在我看来,replace功能已被破坏,我想知道我做错了什么。

以下代码演示了两种情况:第一个容器包含数据副本(正常工作),第二个容器包含指向数据的指针(失败)。

using namespace boost::multi_index;
using boost::multi_index_container;

struct Data
{
    int key1;
    int key2;
};

using DataContainer =
    multi_index_container<
        Data,
        indexed_by<
            hashed_unique<member<Data, int, &Data::key1>>,
            hashed_unique<member<Data, int, &Data::key2>>>>;

using DataPtrContainer = 
    multi_index_container<
        Data*,
        indexed_by<
            hashed_unique<member<Data, int, &Data::key1>>,
            hashed_unique<member<Data, int, &Data::key2>>>>;

TEST(DummyTest, Test1)
{
    Data data{1,2};
    DataContainer dataContainer;
    dataContainer.insert(data);

    EXPECT_EQ(1, dataContainer.get<0>().find(1)->key1);
    EXPECT_EQ(2, dataContainer.get<0>().find(1)->key2);

    auto iter = dataContainer.get<0>().find(1);
    Data d = *iter;
    d.key2 = 5;
    dataContainer.replace(iter, d);

    EXPECT_EQ(1, dataContainer.get<1>().find(5)->key1);
    EXPECT_EQ(5, dataContainer.get<1>().find(5)->key2);

}

TEST(DummyTest, Test2)
{
    Data* data = new Data{1,2};
    DataPtrContainer dataContainer;
    dataContainer.insert(data);

    EXPECT_EQ(1, (*dataContainer.get<0>().find(1))->key1);
    EXPECT_EQ(2, (*dataContainer.get<0>().find(1))->key2);

    auto iter = dataContainer.get<0>().find(1);
    Data* d = *iter;
    d->key2 = 5;
    dataContainer.replace(iter, d);

    EXPECT_EQ(1, (*dataContainer.get<1>().find(5))->key1);  // fail as the iterator not dereferencable
    EXPECT_EQ(5, (*dataContainer.get<1>().find(5))->key2); // fail as the iterator not dereferencable

}

2 个答案:

答案 0 :(得分:3)

好的,这不是Boost.MultiIndex中的错误,而是代码中的细微合同违规。无指针版本:

auto iter = dataContainer.get<0>().find(1);
Data d = *iter;
d.key2 = 5;
dataContainer.replace(iter, d);

将所包含的值的副本复制到d,修改它然后再用它来替换:到目前为止一直很好。但指针版本正在打破不变量:

auto iter = dataContainer.get<0>().find(1);
Data* d = *iter;
d->key2 = 5;  // #1: invariant breach here
dataContainer.replace(iter, d); // #2: unexpected behavior ensues

在#1中,您在未经其同意或不知情的情况下修改dataContainer的内部密钥:一旦您完成了此操作,触摸的元素就会被错误地编入索引。这类似于在无指针版本中抛出常量:

auto iter = dataContainer.get<0>().find(1);
const Data& d = *iter;
const_cast<Data&>(d).key2 = 5;

因此,当执行#2时,datacontainer不会怀疑您已更改其密钥,只需验证您建议的替换d是否与其已有的相同,以及什么都不做(没有重新索引)。

答案 1 :(得分:2)

使用modify函数(如下所示)似乎可以实现相同的效果。这是一个解决方法而不是答案,我会接受任何可以使用replace获得相同结果的答案。

TEST(DummyTest, Test2)
{
    Data* data = new Data{1,2};
    DataPtrContainer dataContainer;
    dataContainer.insert(data);

    EXPECT_EQ(1, (*dataContainer.get<0>().find(1))->key1);
    EXPECT_EQ(2, (*dataContainer.get<0>().find(1))->key2);

    auto iter = dataContainer.get<0>().find(1);
    //Data* d = *iter;
    //d->key2 = 5;
    //dataContainer.replace(iter, d);
    dataContainer.modify(iter, [](Data* data){ data->key2 = 5; });

    EXPECT_EQ(1, (*dataContainer.get<1>().find(5))->key1);
    EXPECT_EQ(5, (*dataContainer.get<1>().find(5))->key2);

}