29

現在、Align_storage を使用して、boost::optional と同様の「オプション」タイプを実装しています。これを達成するために、私は次のようなクラスメンバーを持っています:

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;

オブジェクトを作成するために新しい配置を使用しますが、返されたポインターをどこにも保存しません。代わりに、次のようにすべてのメンバー関数でオブジェクトの基になる型にアクセスします (明らかに、Optional 型にも格納されているブール値フラグを介してオブジェクトが有効であることを確認します)。

T const* operator->() const {
    return static_cast<T const*>(static_cast<void const*>(&t_));
}

私の質問は、これが安全かどうかです。私の理解では、placement new を使用すると、オブジェクトの「動的タイプ」が変更されます。そのタイプを使用してメモリにアクセスし続ける限り、問題はありません。ただし、新しい配置から返されたポインターを保持する必要があるかどうか、またはアクセスする必要があるときはいつでも基になる型にキャストできるかどうかは明確ではありません。私は C++11 標準のセクション 3.10 を読みましたが、確かに標準語に堪能ではありません。

可能であれば、回答で標準を参照していただければ幸いです(夜の睡眠に役立ちます:P)。

4

1 に答える 1

14

ABICTの使用は安全です。

  • タイプTのオブジェクトをnewに配置すると、渡されたアドレスから始まるオブジェクトが作成されます。

§5.3.4/10は言う:

new-expressionは、要求されたスペースの量をstd::size_t型の最初の引数として割り当て関数に渡します。その引数は、作成されるオブジェクトのサイズ以上でなければなりません。オブジェクトが配列である場合にのみ、作成されるオブジェクトのサイズよりも大きくなる可能性があります。

非配列オブジェクトの場合、割り当てられるサイズはオブジェクトのサイズより大きくすることはできないため、オブジェクトの表現は、収まるように割り当てられたメモリの先頭から開始する必要があります。

配置newは、「割り当て」の結果として渡されたポインタ(§18.6.1.3/ 2を参照)を返すため、構築されたオブジェクトのオブジェクト表現はそのアドレスから始まります。

  • static_cast<>T*オブジェクトが完全なオブジェクトである場合、タイプ間の暗黙的な変換とvoid*、オブジェクトへのポインターとそのストレージへのポインターの間の変換。

§4.10/2は言う:

タイプ「pointertocvT」(Tはオブジェクトタイプ)のprvalueは、タイプ「pointertocvvoid」のprvalueに変換できます。「pointertocvT」を「pointertocvvoid」に変換した結果は、オブジェクトがタイプTの最も派生したオブジェクト(1.8)であるかのように、タイプTのオブジェクトが存在する保管場所の開始点を示します。 [...]

これは、述べられているように変換する暗黙の変換を定義します。さらに§5.2.9[expr.static.cast]/4はstatic_cast<>、明示的な変換を定義します。暗黙的な変換は、暗黙的な変換と同じ効果を持つように存在します。

それ以外の場合、宣言 が整形式であれば、発明された一時変数(8.5)の場合、式は形式の aを使用してe明示的に型に変換できます。このような明示的な変換の効果は、宣言と初期化を実行し、変換の結果として一時変数を使用することと同じです。[...]Tstatic_caststatic_cast<T>(e)T t(e);t

static_cast<>(fromvoid*からT*)の場合、§5.2.9/13は次のように述べています。

タイプ「pointertocv1void」のprvalueは、タイプ「pointer to cv2 T」のprvalueに変換できます。ここで、Tはオブジェクトタイプであり、cv2はcv1と同じかそれ以上のcv-qualificationです。[...]「cvvoidへのポインタ」に変換されたオブジェクトへのポインタ型の値は、おそらく異なるcv修飾で、元の値を持つ必要があります。

したがってvoid*、オブジェクトのストレージを指す場合(これは、aからオブジェクトへTの暗黙の変換から生じるポインター値であり、そのaからaへのポインターは、オブジェクトへの有効なポインターを生成します。T*static_castT*

あなたの質問に戻ると、前のポイントはあなたが持っているならそれを意味します

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;

T* pT = new (&t_) T(args...);
void * pvT = pT;

それから

  • のストレージは、のストレージの*pT最初のsize(T)バイトを正確にオーバーレイするt_ため、pvT == pvt_
  • pvt_ == static_cast<void*>(&t_)
  • static_cast<T*>(pvT) == pT
  • まとめると、static_cast<T*>(static_cast<void*>(&t_)) == pT
于 2013-01-28T07:25:18.593 に答える