Где я должен поместить специализированный std::hash для пользовательского типа
Я искал много страниц, и я думаю, что знал, как написать std::hash. Но я не знаю, где это поставить.
Пример представлен здесь http://en.cppreference.com/w/cpp/utility/hash.
Тем не менее, я определил свой тип Instance
в пространстве имен ca
в файле instance_management.h
, Я хочу использовать unordered_set<Instance>
в том же файле в другом классе InstanceManager
, Поэтому я пишу следующий код:
namespace std
{
template <> struct hash<ca::Instance>
{
size_t operator()(const ca::Instance & instance) const
{
std::size_t seed = 0;
// Some hash value calculation here.
return seed;
}
};
} // namespace std
Но куда мне его положить? Я пробовал много мест, но все не удалось.
Я использую Visual Studio 2013. Я пытался поместить предыдущий код в некоторых местах, но все не смогли его скомпилировать.
// location 1
namespace ca
{
class Instance {...}
class InstanceManager
{
// ... some other things.
private unordered_set<Instance>;
}
}
// location 2
3 ответа
Спасибо всем.
Я нашел причину и решил проблему как-то: визуальная студия приняла InstanceHash
когда я определял instances_
, Так как я менял использование set
в unordered_set
Я забыл уточнить InstanceHash
когда я пытался получить const_iterator
поэтому на этот раз компилятор попытался использовать std::hash<>
вещи и не удалось. Но компилятор не нашел строку, используя const_iterator
поэтому я ошибочно подумал, что не принял InstanceHash
когда я определял instances_
,
Я также пытался специализировать std::hash<>
для экземпляра класса. Однако эта специализация требует как минимум объявления класса ca::Instance
и некоторые из его функций-членов для вычисления значения хеш-функции. После этой специализации, определение класса ca::InstanceManage
буду использовать это.
Сейчас я обычно помещаю объявления и реализации почти всех классов и функций-членов вместе. Итак, что мне нужно сделать, это, вероятно, разделить ca
объем пространства имен на 2 части и положить std{ template <> struct hash<ca::Instance>{...}}
в середине.
Есть несколько способов.
специализация std::hash
В своем коде убедитесь, что ваш std::hash<Instance>
специализации предшествует сразу Instance
определение класса, а затем с использованием unordered_set
контейнер, который использует его.
namespace ca
{
class Instance {...};
}
namespaces std {
template<> hash<Instance> { ... };
}
namespace ca {
class InstanceManager
{
// ... some other things.
private unordered_set<Instance>;
}
}
Одним из недостатков является то, что при передаче std::hash<ca::Instance>
к другим функциям. Причина в том, что связанное пространство имен (ca
) из всех аргументов шаблона std::hash
может использоваться во время поиска имени (ADL). Такие ошибки немного редки, но если они возникают, их трудно отладить.
Смотрите этот вопрос и ответы для более подробной информации.
Передавая свой хэш unordered_set
struct MyInstanceHash { ... };
using MyUnorderedSet = std:unordered_set<Instance, MyInstanceHash>;
Здесь вы просто передаете свою собственную хеш-функцию в контейнер и покончите с этим. Недостатком является то, что вы должны явно указать свой собственный контейнер.
С помощью hash_append
Обратите внимание, однако, что предложение по Стандарту N3980 в настоящее время ожидает рассмотрения. Это предложение отличается гораздо более совершенным дизайном, в котором используется универсальная хеш-функция, которая принимает произвольный поток байтов для хеширования его параметром шаблона (фактическим алгоритмом хеширования).
template <class HashAlgorithm>
struct uhash
{
using result_type = typename HashAlgorithm::result_type;
template <class T>
result_type
operator()(T const& t) const noexcept
{
HashAlgorithm h;
using std::hash_append;
hash_append(h, t);
return static_cast<result_type>(h);
}
};
Пользовательский класс X должен обеспечить hash_append
через который он представляет собой поток байтов, готовый к хешированию универсальным хешером.
class X
{
std::tuple<short, unsigned char, unsigned char> date_;
std::vector<std::pair<int, int>> data_;
public:
// ...
friend bool operator==(X const& x, X const& y)
{
return std::tie(x.date_, x.data_) == std::tie(y.date_, y.data_);
}
// Hook into the system like this
template <class HashAlgorithm>
friend void hash_append(HashAlgorithm& h, X const& x) noexcept
{
using std::hash_append;
hash_append(h, x.date_);
hash_append(h, x.data_);
}
}
Для получения дополнительной информации см. Презентацию автора @HowardHinnant на CppCon14 ( слайды, видео). Полный исходный код автора и Bloomberg доступен.
Не специализируется std::hash
вместо этого напишите свой собственный объект хеш-функции (см. Edge_Hash
ниже) и объявите unordered_set
с двумя аргументами шаблона.
#include <unordered_set>
#include <functional>
namespace foo
{
// an edge is a link between two nodes
struct Edge
{
size_t src, dst;
};
// this is an example of symmetric hash (suitable for undirected graphs)
struct Edge_Hash
{
inline size_t operator() ( const Edge& e ) const
{
static std::hash<size_t> H;
return H(e.src) ^ H(e.dst);
}
};
// this keeps all edges in a set based on their hash value
struct Edge_Set
{
// I think this is what you're trying to do?
std::unordered_set<Edge,Edge_Hash> edges;
};
}
int main()
{
foo::Edge_Set e;
}
Связанные посты, например: