如何更新boost multi_index_container的值?

时间:2017-10-13 09:43:34

标签: c++ boost

我使用multi_index_container来跟踪插入顺序并进行映射工作(如LinkedMap of Java)。

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>

#include "CppUnitTest.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;

template<typename KeyType, typename MappedType>
struct LinkedMap {
    typedef std::pair<KeyType, MappedType> value_type;
    typedef boost::multi_index_container<
        value_type,
        boost::multi_index::indexed_by<
        boost::multi_index::random_access<>,
        boost::multi_index::ordered_unique<
        boost::multi_index::member<value_type, KeyType, &value_type::first>
        >
        >
    > type;
};

TEST_CLASS(LinkedMapTest) {
public:
    TEST_METHOD(ShouldUpdateEntry) {
        LinkedMap<int, std::wstring>::type userMap;
        userMap.push_back(std::make_pair(1, L"John"));
        userMap.push_back(std::make_pair(1, L"Tom")); // 2nd push_back
        auto& idToNameMap = userMap.get<1>();
        auto& iter = idToNameMap.find(1);
        Assert::AreEqual(std::wstring(L"Tom"), iter->second);
    }
};

测试用例失败。

  

结果消息:断言失败。预期:其中汤姆&GT;实际:其中约翰&GT;

这意味着第二个push_back不会更新该值。

我的代码出了什么问题?我怎么能实现LinkedMap?

2 个答案:

答案 0 :(得分:1)

您指定的有序索引是唯一

boost::multi_index::ordered_unique<
    boost::multi_index::member<value_type, KeyType, &value_type::first>
 >

使其不唯一以允许重复键:

boost::multi_index::ordered_non_unique<
    boost::multi_index::member<value_type, KeyType, &value_type::first>
>

或者,如果您的密钥需要是唯一的,并且您想要更新现有条目,您可以执行类似(未经测试)的操作:

auto& idToNameMap = userMap.get<1>();
auto& iter = idToNameMap.find(1);
if(iter != idToNameMap.end()) {
   idToNameMap.modify(iter, [](auto& p){p->second = "Tom";});
} 

以下是另一种方法,您可能会发现更多可发送的(再次,未经测试):

LinkedMap::iterator push_back_or_update(LinkedMap& m,const LinkedMap::value_type& x) {
    auto r=m.push_back(x);
    if(!r.second) m.replace(r.first, x);
    return r.first;
}

答案 1 :(得分:1)

  • 修复了错误
  • 用断言替换了Windows特定的测试(所以我可以在我的系统上测试它)
  • 通过索引视图添加了替换演示
  • 添加了标签

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <cassert>
#include <string>
#include <utility>

struct by_key {};
struct by_value {};


template<typename KeyType, typename MappedType>
struct LinkedMap {
    using tag_by_key = boost::multi_index::tag<by_key>;
    using tag_by_value = boost::multi_index::tag<by_value>;
    typedef std::pair<KeyType, MappedType> value_type;
    typedef boost::multi_index_container
    <
        value_type,
        boost::multi_index::indexed_by
        <
            boost::multi_index::random_access<tag_by_value>,
            boost::multi_index::ordered_unique
            <
                tag_by_key,
                boost::multi_index::member<value_type, KeyType, &value_type::first>
            >
        >
    > type;
};

int main()
{
    LinkedMap<int, std::wstring>::type userMap;
    userMap.push_back(std::make_pair(1, L"John"));

    // should not overwrite - as per std::map
    auto ib = userMap.push_back(std::make_pair(1, L"Tom"));
    assert(ib.second == false); // 2nd push_back should fail

    auto overwrite = [&](int key, auto&& val)
    {
        auto& idToNameMap = userMap.get<by_key>();
        auto iter = idToNameMap.find(key);
        if (iter == idToNameMap.end())
        {
            auto ib = userMap.push_back(std::make_pair(1, std::forward<decltype(val)>(val)));
            assert(ib.second);
        }
        else
        {
            idToNameMap.replace(iter, 
                std::make_pair(key, std::forward<decltype(val)>(val)));
        }
    };


    overwrite(1, L"Tom");
    auto& idToNameMap = userMap.get<by_key>();
    auto iter = idToNameMap.find(1);
    assert(std::wstring(L"Tom") == iter->second);
}