Как сделать 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