web-dev-qa-db-ja.com

polymorphic_allocator:いつ、なぜそれを使うべきですか?

ここcppreferenceのドキュメントです。 here は作業ドラフトです。

polymorphic_allocatorの本当の目的と、それをいつ/なぜ/どのように使用すべきかを理解していなかったことを認めなければなりません。
例として、 pmr::vector には次のシグネチャがあります。

namespace pmr {
    template <class T>
    using vector = std::vector<T, polymorphic_allocator<T>>;
}

polymorphic_allocatorは何を提供しますか?昔ながらのstd::pmr::vectorに関して、std::vectorは何を提供しますか?今までできなかった今、私は何ができますか?
そのアロケーターの本当の目的は何ですか?実際に使用するのはいつですか?

77
skypjack

Cppreferenceからの選択引用:

このランタイムポリモーフィズムにより、polymorphic_allocatorを使用するオブジェクトは、同じ静的アロケータータイプにもかかわらず、実行時に異なるアロケータータイプを使用するかのように動作できます。

「通常の」アロケーターの問題は、コンテナーのタイプを変更することです。特定のアロケーターでvectorが必要な場合は、Allocatorテンプレートパラメーターを使用できます。

auto my_vector = std::vector<int,my_allocator>();

問題は、このベクトルが異なるアロケーターを持つベクトルと同じタイプではないことです。たとえば、デフォルトのアロケーターベクトルを必要とする関数に渡したり、同じ変数/ポインターに異なるアロケータータイプの2つのベクトルを割り当てることはできません。例:

auto my_vector = std::vector<int,my_allocator>();
auto my_vector2 = std::vector<int,other_allocator>();
auto vec = my_vector; // ok
vec = my_vector2; // error

ポリモーフィックアロケータは、テンプレートメカニズムではなく動的ディスパッチを介してアロケータの動作を定義できるメンバーを持つ単一のアロケータタイプです。これにより、特定のカスタマイズされた割り当てを使用するが、まだ一般的なタイプのコンテナを持つことができます。

アロケーターの動作のカスタマイズは、アロケーターにstd::memory_resource *を与えることで行われます:

// define allocation behaviour via a custom "memory_resource"
class my_memory_resource : public std::pmr::memory_resource { ... };
my_memory_resource mem_res;
auto my_vector = std::pmr::vector<int>(0, &mem_res);

// define a second memory resource
class other_memory_resource : public std::pmr::memory_resource { ... };
other_memory_resource mem_res_other;
auto my_other_vector = std::pmr::vector<int>(0, &mes_res_other);

auto vec = my_vector; // type is std::pmr::vector<int>
vec = my_other_vector; // this is ok -
      // my_vector and my_other_vector have same type

私が見るように、主な残りの問題は、std::pmr::コンテナーが、デフォルトのアロケーターを使用する同等のstd::コンテナーとまだ互換性がないことです。コンテナで動作するインターフェイスを設計するときに、いくつかの決定を行う必要があります。

  • 渡されたコンテナにはカスタム割り当てが必要になる可能性がありますか?
  • もしそうなら、(任意のアロケータを許可するために)テンプレートパラメータを追加する必要がありますか、ポリモーフィックアロケータの使用を義務付ける必要がありますか?

テンプレートソリューションでは、ポリモーフィックアロケータを含むanyアロケータを使用できますが、その他の欠点(生成されたコードサイズ、コンパイル時間、コードをヘッダーファイルで公開する必要があります。外側の問題)。一方、ポリモーフィックアロケータソリューションでは、ポリモーフィックアロケータmustを使用する必要があります。これにより、デフォルトのアロケーターを使用するstd::コンテナーを使用できなくなり、レガシーコードとのインターフェースに影響を与える可能性があります。

通常のアロケーターと比較して、ポリモーフィックアロケーターには、memory_resourceポインターのストレージオーバーヘッド(ほとんどの場合、無視できる可能性が高い)や、割り当てのための仮想関数ディスパッチのコストなど、若干の小さなコストがあります。実際、主な問題は、多相アロケーターを使用しないレガシーコードとの互換性の欠如でしょう。

66
davmac

polymorphic_allocatorはカスタムアロケーターに対するものであり、std::functionは直接的な関数呼び出しに対するものです。

宣言の時点でどちらを決定する必要なく、コンテナーでアロケーターを使用できます。したがって、複数のアロケーターが適切な場合は、polymorphic_allocatorを使用できます。

インターフェイスを簡素化するために使用するアロケーターを非表示にしたい場合もあれば、さまざまなランタイムの場合に交換できるようにしたい場合もあります。

最初にアロケーターを必要とするコードが必要です。次に、pmrベクトルを検討する前に、使用されているものを交換できるようにする必要があります。

多相アロケーターの欠点の1つは、polymorphic_allocator<T>::pointerは常にただT*。つまり、 ファンシーポインター では使用できません。 vectorの要素を共有メモリに配置し、 boost::interprocess::offset_ptrs 、通常の古い非ポリモーフィックアロケーターを使用する必要があります。

したがって、ポリモーフィックアロケータを使用すると、コンテナの静的タイプを変更せずに割り当ての動作を変更できますが、割り当ての制限があります。

2
Maxpm