c++ - shared_ptrの削除機能は、カスタムアロケーターによって割り当てられたメモリに保存されていますか?




language-lawyer c++17 (2)

C ++ 11のutil.smartptr.shared.const / 9:

効果:オブジェクトpと削除者dを所有するshared_ptrオブジェクトを構築します。 2番目と4番目のコンストラクタは、aのコピーを使用して、内部使用のためにメモリを割り当てます。

2番目と4番目のコンストラクターには次のプロトタイプがあります。

template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

最新のドラフトでは、util.smartptr.shared.const / 10は私たちの目的にとって同等です。

効果:オブジェクトpと削除者dを所有するshared_ptrオブジェクトを構築します。 Tが配列型でない場合、最初と2番目のコンストラクターはpでshared_from_thisを有効にします。 2番目と4番目のコンストラクタは、aのコピーを使用して、内部使用のためにメモリを割り当てます。 例外がスローされると、d(p)が呼び出されます。

そのため、割り当てられたメモリに割り当てる必要がある場合は、アロケータが使用されます。 標準の現在の状態と関連する欠陥レポートを見ると、それは必須ではありませんが、委員会が想定しているようです。

  • shared_ptr のインターフェースは、制御ブロックが存在せず、すべての shared_ptrweak_ptr がリンクリストに置かれる実装を許可するように設計されていますが、実際にはそのような実装はなく、たとえば use_count が共有されたもの。

  • 削除機能は、構築可能な移動のみが必要です。 したがって、 shared_ptr 複数のコピーを shared_ptr ことはできません。

特別に設計された shared_ptr 削除者を配置し、特別な shared_ptr が削除されたときにそれを移動する実装を想像することは可能ですが、本当に奇妙です。特に、使用カウントのためにおそらく制御ブロックが必要です(おそらく可能ですが、使用回数で同じことを行うのはさらに奇妙です)。

私が見つけた関連DR:545、575、2434(すべての実装が制御ブロックを使用していることを認識し、マルチスレッドの制約がある程度それを義務付けているように見える)、 2802 (削除者の要件を構築可能にするだけで、それを防ぐ削除者が複数の shared_ptr 間でコピーされる実装。

たとえば、カスタムアロケーター カスタムデリータを備えた shared_ptr がある します。

標準では、削除プログラムを保存する場所について説明するものは何も見つかりません。カスタムアロケータが削除プログラムのメモリに使用されるとは言わず、保存しないとは言い ませ ん。

これは指定されていないのですか、それとも何か不足していますか?


これは不特定だと思います。

関連するコンストラクターの指定方法は次のとおりです。 [util.smartptr.shared.const]/10

template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

効果: オブジェクト p と削除者 d を所有する shared_ptr オブジェクトを構築します。 T が配列型でない場合、最初と2番目のコンストラク shared_from_thisp shared_from_this を有効にします。 2番目と4番目のコンストラクタ は、aのコピーを使用して、内部使用のためにメモリを割り当てます 。 例外がスローされると、 d(p) が呼び出されます。

今、私の解釈では、実装が内部使用のためにメモリを必要とするとき、それを使用 a ことによってそうする a です。 実装がこのメモリを使用してすべてを配置する必要があるという意味ではありません。 たとえば、この奇妙な実装があるとします:

template <typename T>
class shared_ptr : /* ... */ {
    // ...
    std::aligned_storage<16> _Small_deleter;
    // ...
public:
    // ...
    template <class _D, class _A>
    shared_ptr(nullptr_t, _D __d, _A __a) // for example
        : _Allocator_base{__a}
    {
        if constexpr (sizeof(_D) <= 16)
            _Construct_at(&_Small_deleter, __d);
        else
            // use 'a' to allocate storage for the deleter
    }
};

この実装は「内部使用のためにメモリを割り当てるためにaのコピーを使用しますか?」 はい、そうです。 を使用しない限り、メモリを割り当てることはありません。 この素朴な実装には多くの問題がありますが、 shared_ptr がポインターから直接構築され、コピーまたは移動または参照されず、他の合併症がない最も単純な場合を除き、アロケーターの使用に切り替えるとしましょう。 重要なのは、有効な実装を想像できないからといって、それが理論的に存在できるということではありません。 私はそのような実装が実際に見つかると言っているのではなく、標準が積極的にそれを禁止しているようには見えないというだけです。





allocator