3

一見すると、これは未定義の動作のように見えます...

#include <iostream>

struct SomeBaseClass
{
    // ...
};

struct MyFakerClass
{
    SomeBaseClass base;
    void foo() { std::cout << "hello" << std::endl; }
};

int main()
{
    MyFakerClass c;
    MyFakerClass *p = static_cast<MyFakerClass *>(static_cast<void *>(&c.base));
    p->foo();
}

...しかし、オブジェクトの最初のフィールドがオブジェクトと同じアドレスを持つことが保証されている場合、これは安全でなければなりませんよね? それとも、介入する他のルールがありますか?

4

1 に答える 1

5

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

(C ++ 11、§9.2、¶21)

したがって、クラスが「標準レイアウト」である限り、これは安全です。

標準レイアウトクラスは、次のようなクラスです。

  • タイプnon-standard-layoutクラス(またはそのようなタイプの配列)または参照の非静的データメンバーはありません。
  • 仮想関数(10.3)および仮想基本クラス(10.1)がなく、
  • すべての非静的データメンバーに対して同じアクセス制御(条項11)があり、
  • 非標準レイアウトの基本クラスはありません。
  • 最も派生したクラスに非静的データメンバーがなく、非静的データメンバーを持つ基本クラスが多くても1つないか、非静的データメンバーを持つ基本クラスがない。
  • 最初の非静的データメンバーと同じタイプの基本クラスはありません。

(C ++ 11、§9、¶7)

仮想のものに関するすべての要件はvptrによるものです:上記のように、多くのコンパイラはそれを最初の隠しメンバーとして配置しますが、どこにでも配置するか、可能であれば、それを完全に省略するか、仮想を実装することができます他の方法でディスパッチするため、仮想は一般にクラスのレイアウトを不特定の方法で変更します。

「同じアクセス制御句」は

アクセス制御が異なる非静的データメンバーの割り当て順序は指定されていません。

(C ++ 11、§9.2、¶15)

これは、コンパイラがアクセス制御によってクラスのメンバーを並べ替え/グループ化できるようにするためだと思います(つまり、コンパイラ内では、クラスのIRに、各アクセス制御指定子の下のメンバーのリストが含まれている可能性があります。元の順序を維持するために-インターリーブされたすべてのパブリック/プライベート/保護されたセクションがグループ化され、たとえば、プライベートメンバーはデフォルトでクラスの前に配置される場合があります)。

非標準レイアウトのメンバー/基本クラスの「再帰的」句は、クラスが全体として標準レイアウトであることを保証するためのものです(基本クラスとメンバーはその一部です)。

この残りの部分がクラスのレイアウトに影響を与える可能性がある理由の詳細については、§9.2を参照してください。


これは、クラスがPODであるよりも弱い条件であることに注意してください(上記のコメントで誤って述べたように)。これは、クラスが標準レイアウトであり、些細なものである場合、クラスはPODであるためです(§9¶10)

于 2012-12-09T23:15:08.107 に答える