38

2つのstd::weak_ptrまたは1つのstd::weak_ptrと1つのstd::shared_ptrを比較して同等性を確認します。

私が知りたいのは、weak_ptr/shared_ptrのそれぞれが指すオブジェクトが同じであるかどうかです。アドレスが一致しない場合だけでなく、基になるオブジェクトが削除されてから偶然同じアドレスで再構築された場合も、比較によって否定的な結果が得られるはずです。

したがって、基本的に、アロケータが同じアドレスを予約している場合でも、このアサーションを保持する必要があります。

auto s1 = std::make_shared<int>(43);
std::weak_ptr<int> w1(s1);

s1.reset();

auto s2 = std::make_shared<int>(41);
std::weak_ptr<int> w2(s2);

assert(!equals(w1,w2));

weak_ptrテンプレートは等式演算子を提供しません、そして私が理解したように、それは正当な理由です。

したがって、単純な実装は次のようになります。

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.expired() && t.lock() == u.lock();
}

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.expired() && t.lock() == u;
}

その間に最初のweak_ptrが期限切れになった場合、0になります。そうでない場合は、weak_ptrをshared_ptrにアップグレードし、アドレスを比較します。

これに伴う問題は、weak_ptrを2回(1回)ロックする必要があることです!時間がかかりすぎるのではないかと思います。

私はこれを思いついた:

template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}


template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}

これは、uの所有者ブロックがtの「前」ではなく、tがuの前ではないかどうかをチェックするため、t==uです。

これは私が意図したとおりに機能しますか?別個のshared_ptrから作成された2つのweak_ptrは、常にこのように等しくないものとして比較されますか?それとも私は何かを逃しましたか?

編集:なぜ私はそもそもこれをしたいのですか?共有ポインターを含むコンテナーが必要であり、その中のオブジェクトへの参照を配布したいと思います。イテレータは無効になっている可能性があるため、使用できません。(整数の)IDを配布することはできますが、それは一意性の問題につながり、マップタイプが必要になり、検索/挿入/削除操作が複雑になります。アイデアは、std :: setを使用し、ポインター自体(ラッパークラスにカプセル化されている)をキーとして提供することです。これにより、クライアントはweak_ptrを使用してセット内のオブジェクトにアクセスできます。

4

1 に答える 1

34

私は完全に誤解したので、この答えを完全に書き直します。これは正しく行うのが難しいことです!

の通常の実装はstd::weak_ptrstd::shared_ptr標準と一致しており、2つのヒープオブジェクト(管理対象オブジェクトと制御ブロック)を持つことです。同じオブジェクトを参照する各共有ポインターには、オブジェクトと制御ブロックへのポインターが含まれ、同様に各弱いポインターが含まれます。制御ブロックは、共有ポインターの数と弱ポインタ​​ーの数の記録を保持し、共有ポインターの数が0に達すると、管理対象オブジェクトの割り振りを解除します。弱いポインタの数も0に達すると、制御ブロック自体の割り当てが解除されます。

これは、共有ポインターまたはウィークポインターのオブジェクトポインターが、実際の管理対象オブジェクトのサブオブジェクト(たとえば、基本クラス、メンバー、または管理対象オブジェクトが所有する別のヒープオブジェクト)を指すことができるという事実によって複雑になります。

S0 ----------______       MO <------+
   \__             `----> BC        |
      \_ _______--------> m1        |
     ___X__               m2 --> H  |
S1 -/      \__ __----------------^  |
    \___ _____X__                   |
    ____X________\__                |
W0 /----------------`---> CB -------+  
                          s = 2 
                          w = 1 

ここでは、管理対象オブジェクトの基本クラスとメンバーをそれぞれ指す2つの共有ポインターと、管理対象オブジェクトが所有するヒープオブジェクトを指す弱いポインターがあります。制御ブロックは、2つの共有ポインターと1つの弱いポインターが存在することを記録します。制御ブロックには、管理対象オブジェクトへのポインターもあります。このポインターを使用して、管理対象オブジェクトが期限切れになったときに削除します。

owner_before/セマンティクスは、owner_less共有ポインターと弱ポインタ​​ーを制御ブロックのアドレスで比較します。これは、ポインター自体が変更されない限り変更されないことが保証されています。すべての共有ポインターが破棄されたために弱ポインタ​​ーが期限切れになった場合でも、すべての弱ポインタ​​ーも破棄されるまで、その制御ブロックは存在し続けます。

したがって、equalsコードは完全に正しく、スレッドセーフです。

shared_ptr::operator==問題は、オブジェクトポインターを比較し、同じ制御ブロックを持つ2つの共有ポインターが(上記のように)異なるオブジェクトを指す可能性があるため、一貫性がないことです。

との一貫性を保つためにshared_ptr::operator==、書き込みt.lock() == uはまったく問題ありません。ただし、それが戻った場合trueでも、弱いポインタが他の共有ポインタの弱いポインタであるかどうかは明確ではないことに注意してください。エイリアスポインタである可能性があるため、次のコードで期限切れになる可能性があります。

==ただし、制御ブロックを比較すると、オーバーヘッドが少なくなり(制御ブロックを調べる必要がないため)、エイリアスポインターを使用していない場合と同じ結果が得られます。


ここの基準には何か欠陥があると思います。とを追加するowner_equalsと、順序付けされていないコンテナでのowner_hash使用が可能になります。2つの弱いポインタが同じ制御ブロックを持っている場合は、制御ブロックポインタとオブジェクトポインタ安全に比較できるため、弱いポインタを比較して等しいかどうかを実際に比較することが賢明になります。両方またはどちらも期限切れになっていないこと。おそらく、標準の次のバージョンのための何か。weak_ptrowner_equals

于 2012-09-06T14:17:38.450 に答える