Как сделать c ++ 11 std :: unordered_set из std :: weak_ptr

У меня есть такой набор: set<weak_ptr<Node>, owner_less<weak_ptr<Node> > > setName;

Работает нормально. Но я бы хотел изменить его на неупорядоченный набор. Тем не менее, я получаю около шести страниц ошибок, когда я делаю это. Есть идеи как это сделать?

Просматривая все страницы сообщений об ошибках, я нашел строки, которые могут помочь.

/usr/include/c++/4.7/bits/functional_hash.h:60:7: error: static assertion failed: std::hash is not specialized for this type
/usr/include/c++/4.7/bits/stl_function.h: In instantiation of ‘bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = std::weak_ptr<Node>]’:

9

Решение

поскольку unordered_sets основаны на хэше, вы должны предоставить хеш функциональный объект для типа данных std :: weak_ptr.

Если вы посмотрите на шаблонные параметры unordered_set

template<class Key,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<Key> >
class unordered_set;

вы заметите, что std :: unordered_set предоставляет вам стандартный std :: hash<> параметр шаблона. Но так как std :: hash предоставляет только специализации для конкретный набор типов данных, возможно, вам придется предоставить свои собственные.

В сообщении об ошибке, которое вы цитировали, сказано, что нет std :: hash<> специализация для std :: weak_ptr<> существует, поэтому вы должны предоставить для этого свою собственную функцию хеширования:

template<typename T>
struct MyWeakPtrHash : public std::unary_function<std::weak_ptr<T>, size_t> {
size_t operator()(const std::weak_ptr<T>& wp)
{
// Example hash. Beware: As zneak remarked in the comments* to this post,
// it is very possible that this may lead to undefined behaviour
// since the hash of a key is assumed to be constant, but will change
// when the weak_ptr expires
auto sp = wp.lock();
return std::hash<decltype(sp)>()(sp);
}
};

Редактировать:
Вам также необходимо предоставить функцию равенства, так как не предусмотрено std :: equal_to для слабого_птр.
Принимая возможный способ сделать это из «Сравнение по равенству std :: weak_ptr» в Stackoverflow:

template<typename T>
struct MyWeakPtrEqual : public std::unary_function<std::weak_ptr<T>, bool> {

bool operator()(const std::weak_ptr<T>& left, const std::weak_ptr<T>& right)
{
return !left.owner_before(right) && !right.owner_before(left);
}
};

Все вместе это дает нам следующее:

std::unordered_set<std::weak_ptr<T>,
MyWeakPtrHash<T>,
MyWeakPtrEqual<T>> wpSet;
3

Другие решения

Короткий и неудачный ответ таков: shared_ptr<> может безопасно использоваться в качестве ключа в неупорядоченном наборе или карте, weak_ptr<> не может и не должна. Никакое количество обмана не может сделать его безопасным.

Это потому что weak_ptrИнтерфейс не предоставляет доступ к общему объекту управления, который является основой для сравнения owner_before() когда используется в упорядоченном наборе или карте.

Хотя может показаться разумным заблокировать указатель, а затем shared_ptr, это не. Если последний shared_ptr выходит из области видимости, значение хэша изменится, что приведет к неопределенному поведению при следующей итерации набора или карты. Скорее всего, это останется незамеченным до тех пор, пока ваш код не будет запущен перед заказчиками, где вы иногда будете получать неожиданную и необъяснимую потерю функциональности, но ваши модульные тесты будут по-прежнему проходить без нареканий, давая вам ложное представление о том, что ваш тестовый охват хорош Ваш код надежен и виноваты пользователи, оборудование или сеть.

Итак, в итоге, если вы собираетесь использовать weak_ptЧтобы построить свои не кеширующие объекты (для которых они превосходны), вам нужно использовать std::set<weak_ptr> и страдать от минимального снижения производительности (хотя в действительности это будет уменьшаться из-за потери производительности, вызванной mutex который защищает набор).

Если вы действительно хотите использовать weak_ptr в качестве неупорядоченного ключа вы должны будете написать свой собственный (подсказка: используйте адрес общего блока управления в качестве основы для хэш-функции).

11

Я не думаю, что предложенная хэш-функция верна. Если все общие указатели на объект исчезают, то weak_ptr<X>::lock() вернет пустой shared_ptr, значение хеша которого, вероятно, равно нулю. Таким образом, хеш-функция может возвращать разные значения во времени.

Я думаю, что правильное решение здесь заключается в использовании
boost::unordered_map<X*, boost::weak_ptr<X>>, Тип X* можно легко использовать как ключ к хэш-карте и weak_ptr<X> поскольку значение дает вам шанс выяснить, существует ли указанный объект до сих пор.

Чтобы сохранить значение этого хеша, вы можете использовать что-то вроде:

if (boost::shared_ptr<X> p = wp.lock()) {
// weak_ptr is still valid
ptrs.insert(std::make_pair(p.get(), p));
}
9

Благодаря lx теперь строится следующий код. Чтобы собрать его с помощью gcc 4.7.2, мне пришлось добавить еще два «const», так что теперь приведенный ниже код должен быть полным рабочим примером помещения std :: weak_ptr в std :: unordered_set.

#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include <functional>

using namespace std;

template<typename T>
struct MyWeakPtrHash : public std::unary_function<std::weak_ptr<T>, size_t> {
size_t operator()(const std::weak_ptr<T>& wp) const
{
auto sp = wp.lock();
return std::hash<decltype(sp)>()(sp);
}
};

template<typename T>
struct MyWeakPtrEqual : public std::unary_function<std::weak_ptr<T>, bool> {

bool operator()(const std::weak_ptr<T>& left, const std::weak_ptr<T>& right) const
{
return !left.owner_before(right) && !right.owner_before(left);
}
};

int main() {
unordered_set<std::weak_ptr<string>, MyWeakPtrHash<string>,
MyWeakPtrEqual<string> > stringset;

shared_ptr<string> shptr(new string("Hi there") );
stringset.insert( shptr );
cout << stringset.size() << endl;
}
-1
По вопросам рекламы [email protected]