如何计算std::weak_ptr的哈希值?

4
我有一段代码,它使用`std::weak_ptr`并将其保存在`std::set`中,这样做很好 -- 而且已经持续了5到7年。最近我想尝试在`std::unordered_set`(实际上是在`f14::F14ValueSet`)中使用它们,为此,我需要对其进行哈希。目前,没有定义`std::hash`,那么我应该怎么办呢?
答案似乎是“只需对控制块进行哈希”,正如这个问题和回答所暗示的那样:为什么C++0x中没有为std::weak_ptr定义std::hash?,但我怎么才能访问控制块呢?在glibc中,它位于`__weak_ptr<>::_M_refcount._M_pi->`,但那是私有的(也是特定于实现的)。还能做什么?
一个答案是“等待”:也许将来会有一个标准的`owner_hash()`用于`std::weak_ptr`,但我更喜欢现在就有的东西。

1
这个回答解决了您的问题吗?如何创建一个包含std :: weak_ptr的c++11 std :: unordered_set - jwezorek
看起来如果你不想等待,你需要实现自己的弱指针类型,以公开适当的数据以生成稳定的哈希。 - JohnFilleau
1
嗯,https://dev59.com/j2Yr5IYBdhLWcg3waJj7 中给出的答案明显是错误的,因为它与C++标准委员会的看法相矛盾。它只在“你不能这样做”的意义上是正确的,但在解释原因方面是错误的。那个答案应该被撤回或修改。 - Linas
2个回答

2

制作自己的增强型弱指针。

它存储哈希值,并支持基于owner_before()==比较。

您必须从shared_ptr创建这些内容,因为没有强引用的弱指针无法被哈希以匹配其所有者; 这可能会创建两个相等但哈希不同的增强型弱指针。

template<class T>
struct my_weak_ptr {
  // weak ptr API clone goes here.  lock() etc.

  // different ctor:
  my_weak_ptr(std::shared_ptr<T>const& sp){
    if(!sp) return;
    ptr=sp;
    hash = std::hash<T*>{}(sp.get());
  }
  std::size_t getHash()const{return hash;}
  friend bool operator<(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
    return lhs.owner_before(rhs);
  }
  friend bool operator!=(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
    return lhs<rhs || rhs<lhs;
  }
  friend bool operator==(my_weak_ptr const& lhs, my_weak_ptr const& rhs){
    return !(lhs!=rhs);
  }
private:
  std::weak_ptr<T> ptr;
  std::size_t hash=0;
};

这些具有稳定、合理的哈希值。当一个已回收的对象指针导致哈希冲突时,只要它们不共享控制块,它们就不会相等。

namespace std{template<class T>struct hash<some_ns::my_weak_ptr<T>>{
  std::size_t operator()(my_weak_ptr<T> const& wp)const{return wp.getHash();}
};}

一个警告:使用别名构造函数可能会导致病态结果。因为相等性基于控制块相等性而不是指针值。


1
将指针值而不是哈希值存储为成员,可以通过指针值实现相等性。虽然这与通常的weak_ptr行为不一致,但对于别名构造函数情况可能更好。 - Alex Guteniev
2
@alex 比较和散列悬挂指针对我来说听起来像是未定义行为。在C ++中,删除的指针的使用限制非常严格。 - Yakk - Adam Nevraumont
1
哦,那么在它仍然有效的时候转换为 uintptr_t。虽然这可能会引起其他(可能只是理论上的)问题。也许你是对的,应该立即进行哈希处理。 - Alex Guteniev
@linas 公有继承允许切片。因此,我不会使用公有继承。API并不是很大。可以使用私有继承和using。此外,我的版本已经具有<,对于set set来说不需要额外的东西。 - Yakk - Adam Nevraumont
啊,好的,关于切片。关于 <:我的代码存在自己的命名空间中,而 operator< 需要在 std 命名空间中使用。 - Linas
显示剩余4条评论

0

所以我尝试实现 - 正如Yakk所建议的 - Adam Nevraumont在他对这个问题的回答中提出的一种继承哈希弱指针实现,包括公共接口。如果我做错了什么,有人可以发表评论吗?

template<class T>
struct HashableWeakPointer : protected std::weak_ptr<T>
{
public:
    // Hash class
    class Hash
    {
    public:
        size_t operator()(HashableWeakPointer const & hashableWeakPointer) const
        {
            return hashableWeakPointer.getHash();
        }
    };


    // constructor
    HashableWeakPointer(std::shared_ptr<T> const & sp)
        : std::weak_ptr<T>(sp)
        , hash(0)
    {
        if (static_cast<bool>(sp))
        {
            hash = std::hash<T*>{}(sp.get());
        }
    }


    // weak_ptr-interface
   void reset() noexcept
   {
       std::weak_ptr<T>::reset();
       hash = 0;
   }

   void swap(HashableWeakPointer & r) noexcept
   {
       std::weak_ptr<T>::swap(r);
       std::swap(hash, r.hash);
   }

   using std::weak_ptr<T>::use_count;
   using std::weak_ptr<T>::expired;
   using std::weak_ptr<T>::lock;

   template< class Y >
   bool owner_before( const HashableWeakPointer<Y>& other ) const noexcept
   {
       return std::weak_ptr<T>::owner_before(static_cast<std::weak_ptr<Y>>(other));
   }

   template< class Y >
   bool owner_before( const std::shared_ptr<Y>& other ) const noexcept
   {
       return std::weak_ptr<T>::owner_before(other);
   }


    // hash-interface
    std::size_t getHash() const noexcept
    {
        return hash;
    }

    // helper methods

    // https://en.cppreference.com/w/cpp/memory/shared_ptr
    // "The destructor of shared_ptr decrements the number of shared owners of the control block. If that counter
    // reaches zero, the control block calls the destructor of the managed object. The control block does not
    // deallocate itself until the std::weak_ptr counter reaches zero as well."
    // So below comparisons should stay save even if all shared_ptrs to the managed instance were destroyed.

    friend bool operator<(HashableWeakPointer const& lhs, HashableWeakPointer const& rhs)
    {
        return lhs.owner_before(rhs);
    }

    friend bool operator!=(HashableWeakPointer const& lhs, HashableWeakPointer const& rhs)
    {
        return lhs<rhs || rhs<lhs;
    }

    friend bool operator==(HashableWeakPointer const& lhs, HashableWeakPointer const& rhs)
    {
        return !(lhs!=rhs);
    }

    friend std::ostream & operator<<(std::ostream & os, const HashableWeakPointer& dt)
    {
        os << "<" << dt.lock().get() << "," << dt.hash << ">";
        return os;
    }

private:

    std::size_t hash;

};

关于使用,以下是一个小样例代码

#include <iostream>
#include <memory>
#include <unordered_map>

typedef unsigned KeyValueType;
typedef HashableWeakPointer<KeyValueType> KeyType;
typedef unsigned ValueType;
typedef std::unordered_map<KeyType, ValueType, KeyType::Hash> MapType;


int main()
{
    std::shared_ptr<KeyValueType> sharedPointer = std::make_shared<KeyValueType>(17);
    ValueType const value = 89;

    MapType map;
    std::pair<MapType::iterator,bool> const inserted = map.insert({sharedPointer, value});
    if (not inserted.second)
    {
        std::cerr << "Element for value " << value << " already existed." << std::endl;
    }

    for (MapType::value_type const & entry : map )
    {
        std::cout << "Key:[" << entry.first << "] Value:[" << entry.second << "]" << std::endl;
    }

    return 0;
}

这将为我输出[使用64位的size_t]:

Key:[<0x1ea4b2817f0,2105794893808>] Value:[89]

可以看到,值指针被用作哈希键[2105794893808 = 0x1ea4b2817f0]。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接