I have the following code:
#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;
}
According to boost documentation
template< class CompatibleKey >
iterator find(const CompatibleKey & x);
A type CompatibleKey is said to be a compatible key of (Hash, Pred) if (CompatibleKey, Hash, Pred) is a compatible extension of (Hash, Pred). This implies that Hash and Pred accept arguments of type CompatibleKey, which usually means they have several overloads of their corresponding operator() member functions.
So I thought that auto it = bm.right.find(1u);
will work. Unfortunately this generates an compilation error:
error: no match for call to (boost::bimaps::container_adaptor::detail::key_to_base_identity<Example, const Example>) (const long unsigned int&)
My question is if it is even possible to use CompatibleKey of a diffrent type than bimap key type? I have already tried to go over the boost headers, unfortunately the implmentation is too complicated for me to comprehend what is going on.
I agree with your reading that the description seemed to suggest that this usage should be allowed.
However after long reading and testing, I cannot see how the code would actually support it. What's more, there's this signature:
template< class CompatibleKey >
bool replace_key(iterator position, const CompatibleKey & x);
Which according to its docs requires "CompatibleKey can be assigned to key_type". That's a clear contradiction of the "minimal requirements" seen earlier.
After coming to the conclusion that apparently it can't work, I remembered seeing the same before...:
WONTFIX In order to deal with compatible keys for hashed indices, you'd need not only transparent equality comparison but also some sort of transparent hash functor such as
struct generic_hash { template<typename T> std::size_t operator()(const T& x)const { boost::hash<T> h; return h(x); } };
but using this is tricky (and dangerous):
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
The reason for the problem is: hashing a
std::string
does not yield the same value has hashing a const char*, soc.find("hello")
does not find the string"hello"
. This is why N3657 applies only to associative containers and has not been extended to unordered associative containers.As for
std::less<void>
, I'm sympathetic to your proposal but would prefer to go in line with the standard, which decided forstd::less<void>
to be explicitly provided by the user rather than the default.
I was a little embarrassed to find my own comment from 2014 there :)