3

テンプレート パラメーターについて簡単な質問があります。次のようにリストを使用するクラス Scheduler を作成しています。

list<PartitionT<CompareJobReady, CompareJobRunning> > partitions;

PartitionT は、priority_queues を使用するテンプレート化されたクラスであり、コンパレーター クラスを使用してこれらのキューをパラメーター化したいと考えています。CompareJobReady と CompareJobRunning はこれらのクラスです (特定の operator() を実装します)。

とにかく、PartitionT はテンプレート化されたクラスなので、任意の型のコンパレータ クラスを渡すことができるようにしたいと考えています。特に、CompareJobReadyEDFVD (CompareJobReady から継承) と CompareJobRunningEDFVD (CompareJobRunning から継承) という 2 つの追加クラスを定義しました。

私が実際に今やりたいことは、次のようなものを書くことができるようになることです:

list<PartitionT<CompareJobReady, CompareJobRunning> > *p = new list<PartitionT<CompareJobReadyEDFVD, CompareJobRunningEDFVD> >();

しかし、コンパイラは、変換が不可能であることを教えてくれます。私の問題に対する最善の解決策は何ですか?

4

2 に答える 2

1

標準ライブラリのコンテナ クラスは、その要素を所有しています。つまり、次の場合です。

class Base {};
class Child : public Base {};

std::list < Base > myList;
std::list < Base > *pMyList;

次に、 の要素はmyList、タイプ のオブジェクト (への参照) としてのみアクセスできますBaseBaseタイプ( 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_ptrshared_ptrmyList2Child

(1) 警告があります。この例には影響しませんが、 boostを参照してください。


Comparer クラスのどこかを選択して汎用プログラミングを本当に実行したい場合は、「コンパイル時間に固執する必要があります」: リストの型 ( の型*p) を固定するのlist<Base>ではなく、汎用 (テンプレートを使用) にする必要があります。その上での使用も一般的でなければなりません。ジェネリック プログラミングはコンパイル時にコードを作成することがすべてであるため、(単純に*) ジェネリック プログラミングと実行時の型の選択を混在させることはできません。

*これを可能にするハックがあり、RTTI を悪用するため非常に遅くなります。

于 2012-11-08T15:55:56.067 に答える
0

タイプにはテンプレートを使用しますが、動作には継承を使用します。構築されたコンパレータクラスを(テンプレート化された引数タイプとして)渡すことができるコンストラクタを提供します。

list<PartitionT<CompareJobReady, CompareJobRunning> > *p = 
  new list<PartitionT<CompareJobReady, CompareJobRunning> >( 
    new CompareJobReadyEDFVD(), new CompareJobRunningEDFVD());
于 2012-11-08T11:58:10.013 に答える