9

と について学ぼうとしていstatic_castますreinterpret_cast

私が正しければ、標準 (9.2.18) はreinterpret_cast、ポッドのデータは安全であると言っています:

を使用して適切に変換された POD 構造体オブジェクトへのポインターは、 reinterpret_castその最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。[注: したがって、POD 構造体オブジェクト内に名前のないパディングが存在する場合がありますが、適切な配置を実現するために必要に応じて、先頭にはありません。— エンドノート]

私の質問は、これをどの程度厳密に解釈するかです。たとえば、レイアウトの互換性は十分ですか? もしそうでなければ、なぜですか?

私にとって、次の例は、厳密な「POD のみが有効である」という解釈が間違っているように見える例を示しています。

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
};

class complex : public complex_base
{  //Not a POD-class (due to constructor and inheritance)
public:
  complex(const double real, const double imag); 
}

double* d = new double[4];
//I believe the following are valid because complex_base is POD
complex_base& cb1 = reinterpret_cast<complex_base&>(d[0]);  
complex_base& cb2 = reinterpret_cast<complex_base&>(d[2]);
//Does the following complete a valid cast to complex even though complex is NOT POD?
complex& c1 = static_cast<complex&>(cb1);
complex& c2 = static_cast<complex&>(cb2);

complex_base::m_dataまた、保護されている場合 (complex_baseポッドではないことを意味します) 、何が壊れる可能性がありますか? [編集: どうすれば自分自身を保護し、そのような破損を検出できますか]

レイアウトの互換性で十分であるように思えますが、これは標準が言っていることではないようです。

編集:答えてくれてありがとう。また、 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htmを見つけるのにも役立ちました

4

2 に答える 2

5

complex_base は POD であるため、以下が有効であると思います

あなたは間違っている。オブジェクトd[0]の最初のメンバーを参照しません。complex_baseしたがって、その配置はオブジェクトにとって十分ではない可能性があるためcomplex_base、そのようなキャストは安全ではありません(引用したテキストでは許可されていません)。

以下は、complex が POD ではない場合でも、complex への有効なキャストを完了しますか?

cb1およびcb2タイプ のオブジェクトのサブオブジェクトを指していないcomplexため、static_castは未定義の動作を生成します。C++03 の 5.2.9p5 を参照

型 "cv1 B" の左辺値が実際に型 D のオブジェクトのサブオブジェクトである場合、その左辺値は型 D の外側のオブジェクトを参照します。それ以外の場合、キャストの結果は未定義です。

関連するタイプが単に適合するだけでは十分ではありません。このテキストでは、POD 構造体オブジェクトを指すポインターと、特定のサブオブジェクトを参照する左辺値について説明しています。complex と complex_base は標準レイアウト オブジェクトです。C++0x 仕様では、引用したテキストの代わりに次のように述べています。

POD らしさの要件は厳しすぎますか?

これは別の質問であり、サンプル コードに関するものではありません。はい、POD 性を要求するのは厳しすぎます。C++0x ではこれが認識され、より緩い「標準レイアウト」という新しい要件が与えられました。complex C++0x の定義により、と complex_baseの両方が標準レイアウト クラスであると思います。C++0x 仕様では、引用したテキストの代わりに次のように述べています。

reinterpret_cast を使用して適切に変換された標準レイアウトの構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。

double私はそれを、実際にはcomplexメンバー (継承によるメンバー)を指すポインターを a にキャストできるように解釈し、 にキャストしますcomplex*。標準レイアウト クラスは、非静的データを含む基本クラスがないか、非静的データを含む基本クラスが 1 つしかないクラスです。したがって、固有の「初期メンバー」が存在します。

于 2011-02-20T23:58:59.360 に答える
1

壊れる可能性があるのは、仮想 dtor を含む仮想関数がある場合、仮想ディスパッチを実装するために、非 POD クラス インスタンスに vtable ポインタがある可能性があることです。vtbl ポインターは通常、非 POD クラスの最初のメンバーになります。

(技術的には、仮想ディスパッチはこのように実装する必要はありません。実際にはそうです。そのため、POD タイプとしての資格について標準が非常に厳密でなければなりません。)

ctor (「8.5.1(1): "集約は、ユーザー宣言コンストラクター (12.1) を持たない配列またはクラス (節 9) です") を持つだけで、何かが POD である資格を失う理由が正直にわかりません。しかし、そうです。

ここにある特定のケースでは、もちろん、キャストを再解釈する必要はありません。代わりに、基本クラスに変換演算子を追加するだけです。

class complex_base  // a POD-class (I believe)
{
public:  
  double m_data[2];
  operator double*() {
    return m_data;
  }
};


complex_base b; // create a complex_base
double* p = b; 

complex_base は double* ではないため、C++ コンパイラは、b を p に代入するために、1 つ (かつ 1 つだけ) のユーザー定義の変換演算子を適用します。つまりp = b、変換演算子を呼び出して、p = b.operator double*()yield します (これは実際には正当な構文であることに注意してください。変換演算子を直接呼び出すことはできますが、そうすべきではないことに注意してください)。

b の内部に直接アクセスできるようになったため、これには疑問があることに注意してください。実際には、const double*、またはコピー、またはスマート コピー オン ライトの「ポインター」または ... を返す場合があります。

もちろん、この場合、とにかく m_data は public であるため、次のように記述した場合よりも悪くはありません。

 double* p = b.m_data;

実際には、complex_base のクライアントはそれを double に変換する方法を知る必要がないため、少しはましです。

于 2011-02-20T23:58:49.660 に答える