Программа не компилирует неупорядоченный набор пар целых чисел, но это делает для целых чисел. Можно ли использовать unordered_set и его функции-члены для пользовательских типов, и как я могу определить это?
#include <unordered_set>
...
class A{
...
private:
std::unordered_set< std::pair<int, int> > u_edge_;
};
error: no matching function for call to 'std::unordered_set<std::pair<unsigned int, unsigned int> >::unordered_set()'
Ваш код компилируется на VS2010 SP1 (VC10), но не скомпилируется с GCC g ++ 4.7.2.
Тем не менее, вы можете рассмотреть boost::hash
от Boost.Functional перемешать std::pair
(с этим дополнением ваш код компилируется также с помощью g ++).
#include <unordered_set>
#include <boost/functional/hash.hpp>
class A
{
private:
std::unordered_set<
std::pair<int, int>,
boost::hash< std::pair<int, int> >
> u_edge_;
};
Не существует стандартного способа вычисления хэша для пары. Добавьте это определение в ваш файл:
struct pair_hash {
inline std::size_t operator()(const std::pair<int,int> & v) const {
return v.first*31+v.second;
}
};
Теперь вы можете использовать это так:
std::unordered_set< std::pair<int, int>, pair_hash> u_edge_;
Это работает, потому что pair<T1,T2>
определяет равенство. Для пользовательских классов, которые не обеспечивают способ проверки на равенство, вам может потребоваться предоставить отдельную функцию для проверки, равны ли два экземпляра друг другу.
Конечно, это решение ограничено парой из двух целых чисел. Вот ссылка на ответ это поможет вам определить более общий способ создания хэша для нескольких объектов.
Проблема в том, что std::unordered_set
использует std::hash
шаблон для вычисления хешей для его записей и нет std::hash
специализация для пар. Так что вам придется сделать две вещи:
std::hash
для вашего типа ключа (std::pair<int, int>
) используя эту функцию.Вот простой пример:
#include <unordered_set>
namespace std {
template <> struct hash<std::pair<int, int>> {
inline size_t operator()(const std::pair<int, int> &v) const {
std::hash<int> int_hasher;
return int_hasher(v.first) ^ int_hasher(v.second);
}
};
}
int main()
{
std::unordered_set< std::pair<int, int> > edge;
}
Вы должны предоставить специализацию для std::hash<>
это работает с std::pair<int, int>
, Вот очень простой пример того, как вы можете определить специализацию:
#include <utility>
#include <unordered_set>
namespace std
{
template<>
struct hash<std::pair<int, int>>
{
size_t operator () (std::pair<int, int> const& p)
{
// A bad example of computing the hash,
// rather replace with something more clever
return (std::hash<int>()(p.first) + std::hash<int>()(p.second));
}
};
}
class A
{
private:
// This won't give you problems anymore
std::unordered_set< std::pair<int, int> > u_edge_;
};
Вам не хватает хеш-функции для std::pair<int, int>>
, Например,
struct bad_hash
{
std::size_t operator()(const std::pair<int,int>& p) const
{
return 42;
}
};
....
std::unordered_set< std::pair<int, int>, bad_hash> u_edge_;
Вы также можете специализироваться std::hash<T>
за std::hash<std::pair<int,int>>
, в этом случае вы можете опустить второй параметр шаблона.
Все остальные ответы здесь предлагают создать хеш-функцию, которая каким-то образом объединяет ваши два целых числа.
Это будет работать, но выдает неуникальные хеши. Хотя это хорошо для вашего использования unordered_set
для некоторых приложений это может быть неприемлемо. В вашем случае, если вы выбрали плохую хеш-функцию, это может привести к множеству ненужных коллизий.
Но вы можете создавать уникальные хеши!
int
обычно 4 байта. Вы можете сделать это явно, используя int32_t
,
Тип данных хеша std::size_t
, На большинстве машин это 8 байтов. Вы можете проверить это после компиляции.
Поскольку пара состоит из двух int32_t
типы, вы можете поместить оба числа в std::size_t
сделать уникальный хеш.
Это выглядит так (я не могу без посторонней помощи вспомнить, как заставить компилятор обрабатывать подписанное значение, как если бы оно было беззнаковым для битовых манипуляций, поэтому я написал следующее для uint32_t
.):
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <utility>struct IntPairHash {
std::size_t operator()(const std::pair<uint32_t, uint32_t> &p) const {
assert(sizeof(std::size_t)>=8); //Ensure that std::size_t, the type of the hash, is large enough
//Shift first integer over to make room for the second integer. The two are
//then packed side by side.
return (((uint64_t)p.first)<<32) | ((uint64_t)p.second);
}
};
int main(){
std::unordered_set< std::pair<uint32_t, uint32_t>, IntPairHash> uset;
uset.emplace(10,20);
uset.emplace(20,30);
uset.emplace(10,20);
assert(uset.size()==2);
}