2

简短回答:用户modify改为,接受答案中的详细信息以及此答案

我正在尝试使用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

}
4

3 回答 3

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未经其同意或不知情的情况下修改了内部键:一旦您这样做了,被触摸的元素就会被错误地索引。这类似于在无指针版本中抛弃 constness:

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

因此,当执行 #2 时datacontainer,它不会怀疑您已经更改了它的键,它只是验证您提议的替换d是否等同于它已经拥有的替换,并且什么都不做(然后不重新索引)。

于 2014-04-01T07:25:39.820 回答
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);

}
于 2014-04-01T05:56:04.227 回答
0

replace () 将与ordered_unique 一起使用。

class FooBar final
{
  public:
    FooBar (uint64_t id, std::string_view name)
      : id_ {id},
        name_ {name}
    {
    }

    uint64_t id () const noexcept { return id_; }

    const std::string & name () const noexcept { return name_; }

    void setName (std::string_view name) { name_ = name; }

  private:
    uint64_t id_;
    std::string name_;
};

    struct FooBarId {};
    struct FooBarName {};

    using FooBars = boost::multi_index::multi_index_container <
        std::shared_ptr <FooBar>,
        boost::multi_index::indexed_by <
            boost::multi_index::hashed_unique <
                boost::multi_index::tag <FooBarId>,
                boost::multi_index::const_mem_fun <
                    FooBar,
                    uint64_t,
                    & FooBar::id
                >
            >,
            boost::multi_index::ordered_unique <
                boost::multi_index::tag <FooBarName>,
                boost::multi_index::const_mem_fun <
                    FooBar,
                    const std::string &,
                    & FooBar::name
                >
            >
        >
    >;

    FooBars fooBars;

    auto fooBar1 = std::make_shared <FooBar> (1, "Test1");
    auto fooBar2 = std::make_shared <FooBar> (2, "Test2");
    auto fooBar3 = std::make_shared <FooBar> (3, "Test3");

    fooBars.emplace (fooBar1);
    fooBars.emplace (fooBar2);
    fooBars.emplace (fooBar3);

    auto it1 = fooBars.get <FooBarName> ().find ("Test2");
    assert (it1 != fooBars.get <FooBarName> ().end ());
    assert ((* it1)->id () == fooBar2->id ());

    fooBar2->setName ("Test4");
    fooBars.get <FooBarName> ().replace (it1, fooBar2);

    assert (fooBars.get <FooBarName> ().find ("Test2") == fooBars.get <FooBarName> ().end ());
    assert (fooBars.get <FooBarName> ().find ("Test4") != fooBars.get <FooBarName> ().end ());

于 2021-11-08T21:29:10.427 回答