У меня есть (потенциально) длинные списки пар данных, которые мне нужно объединить (и выполнить некоторую логику), чтобы не было дубликатов. Спаривания были int
типа, но из-за роста количества данных, я преобразую его в пары size_t
и, таким образом, мой тип данных теперь объявлен как pair<size_t, size_t>
,
Код, ранее проверенный на уникальность, имея hash_set
и исследуя это, чтобы видеть, было ли данное соединение уже замечено и обработано. В качестве ключа его удобно использовать INT64
и построил ключ, используя сдвиг битов и упаковку:
INT64 key = ((INT64)pairsListEntry->first) << 32 | pairsListEntry->second;
Это работало хорошо, так как два int
отлично вписывается в INT64
и в результате уникальный ключ. Но по понятным причинам это больше не работает.
Чтобы приспособиться к новым размерам, я попытался провести рефакторинг и объявить hash_set
как:
std::hash_set<pair<size_t, size_t>> m_seenPairs;
Это, однако, не удается при компиляции кода, который создает экземпляр этого класса со следующим сообщением об ошибке:
C: \ Program Files (x86) \ Microsoft Visual Studio 9.0 \ VC \ include \ xhash (71): ошибка C2440: «приведение типа»: невозможно преобразовать из «const std :: pair»<_Ty1, _Ty2> ‘до’ size_t ‘
Это происходит глубоко внутри реализации STL, в следующей функции (на return
линия):
template<class _Kty> inline
size_t hash_value(const _Kty& _Keyval)
{ // hash _Keyval to size_t value one-to-one
return ((size_t)_Keyval ^ _HASH_SEED);
}
Причина довольно ясна: pair<T1, T2>
не знает, как бросить size_t
для того, чтобы вычислить хэш-код.
На данный момент, я застрял на том, как заставить его работать. Гугл-фу не особо облажается. Я видел пару постов на ТАК с std::map
а также pair
, но там, кажется, «просто работать».
Среда VS2008, неуправляемая цель платформы x64.
Я пытался предоставить свой собственный компаратор, как я видел пост, который выглядел по крайней мере едва похожим в дальнейшем:
struct pairs_equal_compare
{
bool operator()(const pair<SampleIdIndex_t, SampleIdIndex_t> & p1, const pair<SampleIdIndex_t, SampleIdIndex_t> & p2) const
{
return (p1.first == p2.first) && (p1.second == p2.second);
}
};
// Holds a set of pairs that are known to exist for deduplication purposes.
stdext::hash_set<pair<SampleIdIndex_t, SampleIdIndex_t>,
stdext::hash_compare<pair<SampleIdIndex_t, SampleIdIndex_t>, pairs_equal_compare>> m_seenPairs;
Это (к тому времени, когда я получил объявления и структуру, должным образом объявленные) привело к точно такой же ошибке — понимая теперь, что это действительно не помогает обойти внутренний вызов hash_value
вычислить хеш-код.
Я также кратко попытался использовать pairs_equal_compare
на месте hash_compare
, но это вызвало больше ошибок компиляции и выглядит как неправильное направление …
Кажется, должен быть разумный способ получить hash_set
работать на pair
(или любые данные нецелого типа), но это ускользает от меня о том, как этого добиться.
Вы также можете использовать подходящий Traits
объект, который ведет себя как hash_compare
т.е. он должен определять два operator()
s:
size_t operator()(const Key &key) const; // This one returns the hash of key
bool operator()(const Key &first,
const Key &second) const; // This one returns true if first is less than second
и две целочисленные константы, которые вы, вероятно, можете просто взять из реализации по умолчанию:
const size_t bucket_size = 4;
const size_t min_buckets = 8;
Увидеть документация hash_compare
.
Код будет выглядеть так
struct pair_comparator{
typedef std::pair<std::size_t, std::size_t> Key;
size_t operator()(const Key &key) const { return /* your hash code here */; }
bool operator()(const Key &first,
const Key &second) const { return first < second; }
const size_t bucket_size = 4;
const size_t min_buckets = 8;
};
stdext::hash_set<
std::pair<std::size_t, std::size_t>,
pair_comparator
> s;
Изменить: документация говорит, что вы также можете получить из специализации hash_compare
и переопределяет только те элементы, которые вам не нравятся, поэтому:
struct pair_comparator : public stdext::hash_compare<std::pair<std::size_t, std::size_t> >{
typedef std::pair<std::size_t, std::size_t> Key;
size_t operator()(const Key &key) const { return /* your hash code here */; }
bool operator()(const Key &first,
const Key &second) const { return first < second; }
};
Что должно избежать проблемы определения const int
члены.
Из коробки, stdext::hash_set<>
работает только с типами, которые неявно преобразуются в size_t
, За std::pair<>
вам нужно будет предоставить аргумент stdext::hash_compare<>
(за stdext::hash_set<>
«s Traits
параметр), который ведет себя как таковой, так как std::pair<>
сам не делает.
Следующее работает для меня с VS2013, и я не понимаю, почему это не будет работать и с VS2008:
#include <cstddef>
#include <utility>
#include <hash_set>
struct pair_hasher
{
typedef std::pair<std::size_t, std::size_t> value_type;
value_type value;
pair_hasher(value_type const& v) : value(v) { }
operator std::size_t() const
{
return (5381 * 33 ^ value.first) * 33 ^ value.second;
}
};
bool operator <(pair_hasher const& a, pair_hasher const& b)
{
return a.value < b.value;
}
Тогда вам нужно будет объявить свой stdext::hash_set<>
экземпляр как таковой:
stdext::hash_set<
std::pair<std::size_t, std::size_t>,
stdext::hash_compare<pair_hasher>
> s;
Для типов, отличных от целочисленных типов для std::pair<>
, Обновить pair_hasher::operator std::size_t
как необходимо (operator <
должно быть хорошо, как есть, до тех пор, пока типы в std::pair<>
сами уже сопоставимы).