Как сделать boost unordered_map для поддержки flyweight <string>
Я пытаюсь сделать следующее:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> > map;
boost::flyweight<std::string> foo(name);
map[foo] = foo;
Но компилятор жалуется:
"ошибка C2665:" boost:: hash_value ": ни одна из 17 перегрузок не могла преобразовать все типы аргументов".
Но я определил следующую функцию:
std::size_t hash_value(const boost::flyweight<std::string> & b)
{
boost::hash<std::string> hasher;
const std::string & str = b.get();
return hasher(str);
}
bool operator==(const boost::flyweight<std::string>& f, const boost::flyweight<std::string> & second)
{
return f.get() == second.get();
}
Но он не компилируется.
Что мне нужно сделать, чтобы сделать boost unordered_map для поддержки flyweight?
[EDIT]
Я получил его для работы со следующим кодом:
struct flyweight_hash
{
std::size_t operator()(const boost::flyweight<std::string> &elm) const
{
boost::hash<std::string> hasher;
const std::string & str = elm.get();
return hasher(str);
}
};
и передал его в качестве параметра шаблона для построения карты:
boost::unordered_map<boost::flyweight<std::string>, boost::flyweight<std::string> , flyweight_hash > map;
В этом случае я не понимаю, как перегрузка hash_value не работала.
Ответы
Ответ 1
boost::hash
вызывает hash_value
через зависимый от аргумента поиск (ADL). Вы пытаетесь определить функцию hash_value
для класса в пространстве имен boost
. Следовательно, ваша функция hash_value
должна войти в это пространство имен, а также для работы ADL. К сожалению, добавление функций к внешнему пространству имен является довольно злым, и его следует избегать. Ваше решение использовать пользовательский хешер кажется прекрасным.
Маленький примерный код для иллюстрации:
namespace boost {
// somewhere in boost
template<typename T>
std::size_t hash(const T& t) {
// call using ADL
// e.g. if called with object of class type foo::bar this will
// pick up foo::hash_value despite the lack of namespace
// qualification
return hash_value(t);
}
}
// your hash_value (presumably in the global namespace)
// not picked up by above call
std::size_t hash_value(boost::flyweight<T>...);
namespace boost {
// this would be picked up but is slightly evil
std::size_t hash_value(boost::flyweight<T>...);
}
Ответ 2
Жаль хешировать то, что уже было хэшировано. Flyweight хранит один экземпляр одинаковых объектов, поэтому более эффективно хешировать адрес этого экземпляра, а не его содержимое. Я делаю следующее (в std
, а не в boost
, поскольку я использую С++ 11, поэтому я расширяю std::hash
, а не boost::hash
):
namespace std
{
template <typename T>
struct hash<boost::flyweight<T, boost::flyweights::no_tracking>>
{
using value_type = boost::flyweight<T, boost::flyweights::no_tracking>;
size_t operator()(const value_type& ss) const
{
hash<const void*> hasher;
return hasher(&ss.get());
}
};
}
Я был подтвержден, что это работает по дизайну, а не случайно: http://lists.boost.org/boost-users/2013/03/78007.php