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

标签 c++ boost boost-multi-index

简短回答:用户修改,接受的答案中的详细信息以及 this answer

我正在尝试使用保存指针类型的 boost::multi_index_container 。在我看来,替换功能已损坏,我想知道我做错了什么。

以下代码演示了两种情况:第一个是保存数据拷贝的容器(工作正常),第二个是保存指向数据的指针的容器(失败)。

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

}

最佳答案

好的,这不是 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 不会怀疑您已更改其 key ,它只是验证您建议的替换 d 是否与其等效已经有了,并且什么也不做(然后不重新索引)。

关于c++ - 为什么 `boost::multi_index replace`不能用于指针类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22777768/

相关文章:

Boost.Asio SSL 非正常关闭

c++ - 使用 Boost 的属性树添加 XML header

c++ - qi::parse boost 改变开始迭代器

c++ - 测量 C++ 中函数调用的执行时间

c++ - 为什么 boost-multi-index 会返回一个错误的迭代器?

c++ - 此代码是否有效的 C++?

c++ - 以重复方式向 std::vector 添加元素

c++ - 使用内部迭代器从 boost 多索引中删除项目时的一致性

c++ - 另一个链接器问题

c++ - &++x 和 &x++ 的区别