15

私はこのようなセットを持っています:set<weak_ptr<Node>, owner_less<weak_ptr<Node> > > setName;

それは正常に動作します。しかし、それを順不同のセットに変更したいと思います。ただし、これを行うと、約 6 ページのエラーが発生します。それを行う方法はありますか?

エラーメッセージのすべてのページを調べた後、役立つ可能性のある行を見つけました。

/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>]’:
4

4 に答える 4

25

簡潔で残念な答えは、 whileshared_ptr<>を順序付けられていないセットまたはマップのキーとして安全に使用できるが、使用できweak_ptr<>ないし、使用してはならないということです。どんなに策略を講じても安全にはなりません。

これは、weak_ptrのインターフェイスが共有コントロール オブジェクトへのアクセスを公開していないためです。これはowner_before()、順序付けられたセットまたはマップで使用される場合の比較の基礎となります。

ポインタをロックしてから をハッシュするのは合理的に思えるかもしれshared_ptrませんが、そうではありません。最後shared_ptrが範囲外になると、ハッシュ値が変更され、次にセットまたはマップが繰り返されるときに未定義の動作が発生します。これは、コードが顧客の前で本番環境に置かれるまで気付かれない可能性が高く、予期せぬ不可解な機能の損失が時折発生しますが、単体テストは問題なく合格し、テスト カバレッジが良好であるという誤った考えを与えます。あなたのコードは信頼できますが、それはユーザー、ハードウェア、またはネットワークのせいです。

したがって、要約すると、weak_ptr を使用して非所有オブジェクト キャッシュ (優れている) を構築する場合は、a を使用する必要があり、std::set<weak_ptr>わずかなパフォーマンス ヒットを被る必要があります (ただし、実際には、これはパフォーマンスの損失によって小さくなります)。mutexセットを保護するによって引き起こされます)。

本当に順序付けされていないキーとして a を使用したい場合はweak_ptr、独自に作成する必要があります (ヒント: ハッシュ関数の基礎として共有制御ブロックのアドレスを使用してください)。

于 2015-06-18T17:49:24.873 に答える
10

提案されたハッシュ関数が正しいとは思いません。オブジェクトへのすべての共有ポインターが消える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));
}
于 2014-03-01T05:48:06.673 に答える
1

受け入れられた解決策であるにもかかわらず、私のものは間違っているため、以下のRichard Hodgesの回答をお読みください。


unordered_setsハッシュベースであるため、std::weak_ptr データ型のハッシュ関数オブジェクトを提供する必要があります

unordered_set template-parameters を見ると

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::weak_ptr<> の std::hash<> 特殊化が存在しないことを示しているため、そのために独自のハッシュ関数を提供する必要があります。

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);
   }
};

編集: weak_ptrのstd::equal_toが提供されていないため、等値関数も提供する必要があります。Stackoverflow の "Equality-compare std::weak_ptr"からこれを行う方法を考えてみましょう:

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;
于 2012-12-04T15:30:15.693 に答える