У меня есть такой набор: 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>]’:
поскольку 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;
Короткий и неудачный ответ таков: shared_ptr<>
может безопасно использоваться в качестве ключа в неупорядоченном наборе или карте, weak_ptr<>
не может и не должна. Никакое количество обмана не может сделать его безопасным.
Это потому что weak_ptr
Интерфейс не предоставляет доступ к общему объекту управления, который является основой для сравнения owner_before()
когда используется в упорядоченном наборе или карте.
Хотя может показаться разумным заблокировать указатель, а затем shared_ptr
, это не. Если последний shared_ptr
выходит из области видимости, значение хэша изменится, что приведет к неопределенному поведению при следующей итерации набора или карты. Скорее всего, это останется незамеченным до тех пор, пока ваш код не будет запущен перед заказчиками, где вы иногда будете получать неожиданную и необъяснимую потерю функциональности, но ваши модульные тесты будут по-прежнему проходить без нареканий, давая вам ложное представление о том, что ваш тестовый охват хорош Ваш код надежен и виноваты пользователи, оборудование или сеть.
Итак, в итоге, если вы собираетесь использовать weak_pt
Чтобы построить свои не кеширующие объекты (для которых они превосходны), вам нужно использовать std::set<weak_ptr>
и страдать от минимального снижения производительности (хотя в действительности это будет уменьшаться из-за потери производительности, вызванной mutex
который защищает набор).
Если вы действительно хотите использовать weak_ptr
в качестве неупорядоченного ключа вы должны будете написать свой собственный (подсказка: используйте адрес общего блока управления в качестве основы для хэш-функции).
Я не думаю, что предложенная хэш-функция верна. Если все общие указатели на объект исчезают, то 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));
}
Благодаря 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;
}