標準ライブラリのコンテナ クラスは、その要素を所有しています。つまり、次の場合です。
class Base {};
class Child : public Base {};
std::list < Base > myList;
std::list < Base > *pMyList;
次に、 の要素はmyList
、タイプ のオブジェクト (への参照) としてのみアクセスできますBase
。Base
タイプ( push_back
、 など)の要素を格納emplace_back
し、参照/コピー ( front
、またはイテレータを介して) を取得できます。たとえば、cppreference/listを参照してください。見てみましょうpush_back
:
void push_back(const value_type&);
value_type
に渡した最初のテンプレート パラメータはどこにありますかstd::list
。あなたの場合、つまりPartitionT < CompareJobReady, CompareJobRunning >
、または上の例ではBase
です。
渡した引数をpush_back
実際にコピーし、そのコピーを新しい要素にします。何故ですか?新しい要素はリストによって所有される可能性があるためです。リスト自体が破棄されると、リストはこの要素を破棄し、2 つのリストを移動/交換すると別のリストに渡すことができます。要素がコピーされておらず、外部から破棄された場合、リストには破棄された要素が含まれます。これは、リストによって与えられた保証を破ることになります (そして、それはあまり良いことではありません)。
別の例 (単純化されているため、あまり正確ではありません):list
アロケーターを介して要素にメモリを割り当てます。ここでのデフォルトのアロケータはstd::allocator<Base>
. type のオブジェクトを格納するために必要なだけのメモリを要素に割り当てますBase
。
のようなことをするpMyList = new std::list < Child >;
と、右側は「へのポインタ」になりますstd::list<Child>
が、左側は「へのポインタ」のタイプになりますstd::list<Base>
。std::list<Base>
は から継承したり、 への変換を定義したりしないためstd::list<Child>
、これら 2 つの型は無関係です。これがかなり良い理由はいくつかあります。その 1 つは、ジェネリック プログラミングが処理する型を知る必要があることです。ポリモーフィズムは抽象化であるため、コンパイル時にどのタイプを扱っているかを知る必要はなく、また知ることもできません。
C++ のポリモーフィズムは、ポインターまたは参照を介して機能します。Child
したがって、この型に依存しない (たとえば型しか知らない) リストにいくつかのオブジェクトを配置したい場合Base
は、それらをポインタとして格納する必要があります。
std::list < std::shared_ptr<Base> > myList2;
myList2.push_back( new Child ); // better not use, there's a caveat (1)
// approach w/o this caveat
std::shared_ptr<Base> pNewChild( new Child ); // or make_shared
myList2.push_back( pNewChild );
ここでa を使用することに注意してください。目的に合っshared_ptr
た if を使用することもできますが、unique_ptr
生の ptr を使用しないでください。これは要素をmyList2
所有し、(Base 型の) オブジェクトを共有所有権で保持するため、間接的にリストにptrsを保存するオブジェクト。生のptrsは所有権を表明しないため、たとえば、誰がそれらを破壊する責任があるかは明らかではありません。「ゼロのルール」の詳細をお読みください。shared_ptr
shared_ptr
myList2
Child
(1) 警告があります。この例には影響しませんが、 boostを参照してください。
Comparer クラスのどこかを選択して汎用プログラミングを本当に実行したい場合は、「コンパイル時間に固執する必要があります」: リストの型 ( の型*p
) を固定するのlist<Base>
ではなく、汎用 (テンプレートを使用) にする必要があります。その上での使用も一般的でなければなりません。ジェネリック プログラミングはコンパイル時にコードを作成することがすべてであるため、(単純に*) ジェネリック プログラミングと実行時の型の選択を混在させることはできません。
*これを可能にするハックがあり、RTTI を悪用するため非常に遅くなります。