【问题标题】:Finding an element in boost::bimaps::bimap by different type than bimap key type通过与 bimap 键类型不同的类型在 boost::bimaps::bimap 中查找元素
【发布时间】:2018-04-26 12:54:16
【问题描述】:

我有以下代码:

#include <boost/bimap/bimap.hpp>
#include <boost/bimap/unordered_multiset_of.hpp>
#include <string>

using namespace boost::bimaps;
using namespace boost;

struct Example
{
    uint64_t id;
};

struct ExampleHash
{
    uint64_t operator()(const Example& item) const
    {
        return item.id;
    }

    uint64_t operator()(const uint64_t item) const
    {
        return item;
    }
};

struct ExampleEq
{
    bool operator()(const Example& l, const Example& r) const
    {
        return l.id == r.id;
    }

    bool operator()(const uint64_t l, const Example& r) const
    {
       return l == r.id;
    }

    bool operator()(const Example& l, const uint64_t r) const
    {
        return operator()(r, l);
    }
};

using BM = bimaps::bimap<
    unordered_multiset_of<std::string>,
    unordered_multiset_of<Example, ExampleHash, ExampleEq>
>;

int main() {
    BM bm;
    bm.insert(BM::value_type("First", Example{1}));

    auto it = bm.right.find(1u);

    return 0;
}

根据boost documentation

template< class CompatibleKey >
iterator find(const CompatibleKey & x);

如果 (CompatibleKey, Hash, Pred) 是 (Hash, Pred) 的兼容扩展,则称类型 CompatibleKey 是 (Hash, Pred) 的兼容键。这意味着 Hash 和 Pred 接受 CompatibleKey 类型的参数,这通常意味着它们有几个对应的 operator() 成员函数的重载。

所以我认为auto it = bm.right.find(1u); 会起作用。不幸的是,这会产生编译错误:

error: no match for call to (boost::bimaps::container_adaptor::detail::key_to_base_identity<Example, const Example>) (const long unsigned int&)

我的问题是,是否甚至可以使用与 bimap 键类型不同类型的 CompatibleKey?我已经尝试过提升标题,不幸的是,实现太复杂了,我无法理解发生了什么。

【问题讨论】:

    标签: c++ boost boost-bimap


    【解决方案1】:

    我同意您的阅读,即描述似乎暗示应该允许这种用法。

    然而,在 long 阅读和测试之后,我看不出代码实际上是如何支持它的。更重要的是,还有这个签名:

    template< class CompatibleKey >
      bool replace_key(iterator position, const CompatibleKey & x);
    

    根据其文档要求“CompatibleKey 可以分配给 key_type”。这与前面看到的“最低要求”明显矛盾。

    在得出显然它行不通的结论后,我记得以前看到过同样的情况......:

    WONTFIX 为了处理散列索引的兼容键,您需要 不仅是透明的平等比较,还有某种 透明散列函数如

    struct generic_hash
    {
      template<typename T>
      std::size_t operator()(const T& x)const
      {
         boost::hash<T> h;
         return h(x);
      }
    };
    

    但使用它很棘手(而且很危险):

    multi_index_container<
      std::string,
      indexed_by<
        hashed_unique<identity<std::string>,generic_hash,std::less<void>>
      >
    > c{"hello"};
    
    std::cout<<*(c.find("hello"))<<"\n"; // crash
    

    问题的原因是:散列一个std::string 不会产生 相同的值具有散列 const char*,所以 c.find("hello") 确实 找不到字符串"hello"。这就是为什么N3657 仅适用于 关联容器,并没有扩展到无序 关联容器。

    至于std::less&lt;void&gt;,我很同情你的提议,但会 更喜欢去符合标准,这决定了 std::less&lt;void&gt; 由用户明确提供,而不是 默认。

    在那里找到我自己 2014 年的评论有点尴尬 :)

    【讨论】:

    • (PS。如果不清楚,Boost Multi Index underlies Boost Bimap
    • 这看起来像是 Boost.Bimap 中的一个错误。 CompatibleKey 可以分配给key_type 的要求不适用于find(或者至少文档没有提及)。
    【解决方案2】:

    我不了解 Boost.Bimap,但使用 Boost.MultiIndex 的等效构造可以按预期工作:

    Live On Coliru

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/hashed_index.hpp>
    #include <boost/multi_index/member.hpp>
    #include <iostream>
    #include <string>
    #include <utility>
    
    using namespace boost::multi_index;
    using namespace boost;
    
    struct Example
    {
        uint64_t id;
    };
    
    struct ExampleHash
    {
        uint64_t operator()(const Example& item) const
        {
            return item.id;
        }
    
        uint64_t operator()(const uint64_t item) const
        {
            return item;
        }
    };
    
    struct ExampleEq
    {
        bool operator()(const Example& l, const Example& r) const
        {
            return l.id == r.id;
        }
    
        bool operator()(const uint64_t l, const Example& r) const
        {
           return l == r.id;
        }
    
        bool operator()(const Example& l, const uint64_t r) const
        {
            return operator()(r, l);
        }
    };
    
    using BM_value_type=std::pair<std::string,Example>;
    
    using BM = multi_index_container<
        BM_value_type,
        indexed_by<
            hashed_non_unique<member<BM_value_type, std::string, &BM_value_type::first>>,
            hashed_non_unique<
              member<BM_value_type,Example,&BM_value_type::second>,
              ExampleHash,ExampleEq
            >
        >
    >;
    
    int main() {
        BM bm;
        bm.insert(BM::value_type("First", Example{1}));
    
        auto it = bm.get<1>().find(1u);
    
        std::cout<<it->second.id<<"\n"; // 1
    
        return 0;
    }
    

    您可能想向 Boost.Bimap 提交请求(这在我看来绝对是一个错误)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-20
      • 1970-01-01
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多