static_cast
クラス間のメモリ レイアウトがコンパイル時にわかっているキャストのみを実行できます。dynamic_cast
実行時に情報をチェックできるため、キャストの正確性をより正確にチェックできるだけでなく、メモリ レイアウトに関する実行時の情報を読み取ることができます。
Base
仮想継承は、との間のメモリ レイアウトを指定するランタイム情報を各オブジェクトに入れますDerived
。次から次へと続きますか、それとも追加のギャップがありますか? このような情報にアクセスできないためstatic_cast
、コンパイラは控えめに動作し、コンパイラ エラーを発生させます。
さらに詳細に:
複雑な継承構造を考えてみましょう。多重継承により、 の複数のコピーが存在しBase
ます。最も典型的なシナリオは、ダイヤモンドの継承です。
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
このシナリオでは、とでBottom
構成され、それぞれに の独自のコピーがあります。上記のすべてのクラスのメモリ構造はコンパイル時に認識され、問題なく使用できます。Left
Right
Base
static_cast
同様の構造を考えてみましょうが、 の仮想継承がありBase
ます。
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
仮想継承を使用すると、Bottom
が作成されたときに、オブジェクト パーツとの間で共有される のコピーが1 つだけ含まれるようになります。オブジェクトのレイアウトは、たとえば次のようになります。Base
Left
Right
Bottom
Base part
Left part
Right part
Bottom part
Bottom
ここで、キャストすることを検討してくださいRight
(これは有効なキャストです)。Right
2 つの部分に分かれているオブジェクトへのポインターを取得します。その間に、(現在は無関係な)部分を含むメモリ ギャップがありますBase
。このギャップに関する情報は、実行時に(通常は と呼ばれる) の非表示フィールドに格納されます。たとえば、ここで詳細を読むことができます。Right
Left
Right
vbase_offset
Right
ただし、スタンドアロンオブジェクトを作成するだけであれば、ギャップは存在しません。
したがって、ポインタだけを示しRight
た場合、それがスタンドアロン オブジェクトなのか、それともより大きなオブジェクトの一部なのか (たとえば ) はコンパイル時にわかりませんBottom
。Right
からに正しくキャストするには、ランタイム情報を確認する必要がありますBase
。それがstatic_cast
失敗する理由であり、失敗しdynamic_cast
ない理由です。
dynamic_cast に関する注意:
static_cast
オブジェクトに関する実行時情報を使用しませんが、使用dynamic_cast
し、存在する必要があります! したがって、後者のキャストは、少なくとも 1 つの仮想関数 (仮想デストラクタなど) を含むクラスでのみ使用できます。