0

Assuming a custom HashableWeakPointer as proposed in this question I would like to ask for further clarification of statements written in an answer to that question.

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

  // different ctor:
  HashableWeakPointer (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<(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);
  }
private:
  std::weak_ptr<T> ptr;
  std::size_t hash=0;
};
  1. "While a recycled object pointer results in a hash collision, so long as they don't share control blocks they won't be equal."

And as the control block of a weak_ptr is not freed until the last referencing weak_ptr [and shared_ptr...] to that instance is destroyed, this should be save with regard to the cited statement - right?

  1. "One warning: Use of aliasing constructor could result in pathological results. As equality is based on control block equality, not pointer value."

But this is not a deficiency of your proposed solution, but rather a question of one's definition of what it means for two weak_ptrs to be equal, isn't it?

Would the following distinguish aliasing smart pointers with somewhat less pathological results?

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

where the second part is only evaluated if the first is false, which thus would mean for (lhs.hash < rhs.hash) being relevant it must (not lhs.owner_before(rhs)) && ((not rhs.owner_before(lhs)) evaluate to true, i.e. the same owner be referenced?

  • The only standard approved way to use a weak_ptr is to get a shared_ptr from it. – Mark Ransom Aug 05 '22 at 16:34
  • One of the important points to having a **key** is that it is **immutable**. If the key can have the rug pulled out from under its feet, the hash value changes, and the invariant is violated. Hijinks and hilarity ensue. Where "hilarity" means late night debugging sessions to figure out why things failed in production, and your boss is calling you every 5 minutes asking "Is it fixed yet?" – Eljay Aug 05 '22 at 16:40
  • 1
    @Eljay only your boss? Must be nice. I'm used to a over a half-dozen intermediate managers, directors, managing directors, directing managers, executive assistants, assistants to the executive assistants, all pinging with "What's the status?", "What's the root cause", "Is it fixed", each trying to win the race to the finish line of announcing progression via org-wide email in efforts to kiss the tails of their superiors and garner promotion for their "hard work", when in reality they never actually did a damn thing but continuously interrupt you. – WhozCraig Aug 05 '22 at 16:47
  • Ok, sorry. I hope this somewhat elaborated question also elaborates the fact that an immutable key is produced from an actual `shared_ptr`. And whether the rug can be pulled, was actually an intended part of above question. Is this still unclear? If so, please let me know. – JohannesWilde Aug 05 '22 at 16:51
  • It is still theoretically possible, though perhaps exceedingly unlikely, that your version of `operator<` would declare two pointers to two distinct `T` subobjects of the same object equal. Such pointers would pass `owner_before` tests (since they share the control block), and `std::hash` for these two pointers might produce the same value due to a collision. – Igor Tandetnik Aug 06 '22 at 22:32

0 Answers0