在 boost::bimaps::bimap 中查找与 bimap 键类型不同的元素

Finding an element in boost::bimaps::bimap by different type than bimap key type

提问人:AndyB 提问时间:4/26/2018 更新时间:4/27/2018 访问量:777

问:

我有以下代码:

#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文档

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?我已经尝试过 boost 标题,不幸的是,实现太复杂了,我无法理解发生了什么。

C++ Boost boost-bimap

评论


答:

0赞 sehe 4/27/2018 #1

我同意你的阅读,即描述似乎表明应该允许这种用法。

但是,经过长时间的阅读和测试,我看不出代码将如何实际支持它。更重要的是,还有这个签名:

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

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

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

不会为了处理哈希索引的兼容键,您需要 不仅是透明的平等比较,而且是某种形式的平等比较 透明哈希函子,例如

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

问题的原因是:哈希 a 不会产生 相同的值具有散列 const char*,也是如此 找不到字符串。这就是为什么 N3657 仅适用于 关联容器,并且尚未扩展到无序容器 关联容器。std::stringc.find("hello")"hello"

至于,我很同情你的提议,但会 更愿意与标准保持一致,该标准决定由用户明确提供,而不是 默认值。std::less<void>std::less<void>

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

评论

0赞 sehe 4/27/2018
(附言。如果不清楚,Boost Multi Index 是 Boost Bimap 的基础)
0赞 Joaquín M López Muñoz 4/27/2018
这看起来像是 Boost.Bimap 中的一个错误。可以分配给的要求不适用于(或者至少文档没有提到它)。CompatibleKeykey_typefind
0赞 Joaquín M López Muñoz 4/27/2018 #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 提交工单(这对我来说绝对是一个错误)。