У меня есть следующий код:
#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;
}
В соответствии с форсировать документацию
template< class CompatibleKey >
iterator find(const CompatibleKey & x);
Тип CompatibleKey называется совместимым ключом (Hash, Pred), если (CompatibleKey, Hash, Pred) является совместимым расширением (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&)
Мой вопрос: возможно ли использовать CompatibleKey другого типа, чем тип ключа bimap? Я уже пытался обойти заголовки наддува, к сожалению, реализация слишком сложна для меня, чтобы понять, что происходит.
Я согласен с вашим чтением, что описание казалось предположить, что это использование должно быть разрешено.
Однако после долго читая и тестируя, я не вижу, как код на самом деле это поддерживает. Более того, есть эта подпись:
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<void>
Сочувствую вашему предложению но буду
предпочитаю идти в соответствии со стандартом, который решил для
std::less<void>
быть явно предоставленным пользователем, а не
по умолчанию.
Я был немного смущен, чтобы найти свой собственный комментарий от 2014 года там 🙂
Я не знаю о Boost.Bimap, но эквивалентная конструкция с использованием Boost.MultiIndex работает как задумано:
#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 (для меня это определенно похоже на ошибку).