8

tl;dr: ジェネリック型Valueのメンバーを含むクラスListEntryを構築したいのですが、Valueはデフォルトで構築可能ではなく、ListEntryは構築方法を知りません。このValueメンバーには決してアクセスしないので、初期化されていなくても問題ありません。

なぜ私はこれをやっているのですか

次のような二重リンクリストを実装しています

template<class Value>
class ListEntry {
  Value value;
  ListEntry<Value> *prev;
  ListEntry<Value> *next;
};
template<class Value>
class List {
  ListEntry<Value> sentinel;
};

リスト エントリ間のリンクは常に、番兵が最後のリスト要素を最初のリスト要素に接続する閉じた円を形成します。Sentinel オブジェクトは、sentinel.prev = &sentinel および sentinel.next = &sentinel で初期化されます。

このようにして、多くの特殊なケースを取り除き、null ポインターがないため、nullptr をチェックする必要がなくなりました。リストの最後 (最後の要素とセンチネルの間) に要素を追加することは特別なケースではなく、2 つの実際の要素の間のリストの中央に要素を追加することと同じです。

したがって、すべての実際のリスト エントリで、値フィールドにはリスト エントリの実際の値が含まれます。それらの場合、コンストラクターでValueオブジェクトを指定することでListEntryを初期化できるため、 Valueをデフォルトで構築可能にする必要はありません。センチネルでは、値フィールドにアクセスすることはありません。しかし残念なことに、Valueはデフォルトで構築可能ではないため、コンパイラはセンチネル オブジェクトを作成できません。

ListEntryの値メンバーをポインター、boost::optional、または同様のものにすることができます。ただし、パフォーマンスの問題があるため、これは好きではありません。パフォーマンス/メモリのコストなしで、Valueをデフォルトで構築可能にする必要なしに、ListEntryにValueを格納する方法についてのアイデアはありますか? コンストラクターを呼び出さずにValueオブジェクトを取得する方法が必要なように思えます。

4

2 に答える 2

7

raw バッファーと配置 new を使用します。

template<class Value>
class ListEntry {
  alignas(Value) char storage[sizeof(Value)];
  ListEntry<Value> *prev;
  ListEntry<Value> *next;
};

の構築Value:

new (entry->storage) Value(/* params */);

の破壊Value:

reinterpret_cast<Value*>(entry->storage)->~Value();
于 2015-04-26T21:36:24.743 に答える
5

これを基本クラスとノード クラスに分割できます。

class ListEntryBase {
    ListEntryBase *prev;
    ListEntryBase *next;
};

template<class Value>
class ListEntry : public ListEntryBase {
    Value value;
};

template<class Value>
class List {
    ListEntryBase sentinel;
};

このようにして、不要な値の作成を回避すると同時に、Valueデフォルトで構築可能にする必要がありません。

于 2015-04-26T21:47:41.087 に答える