C++のキャストについてあまりよく知らなかったので調べた。
先にまとめ:
- reinterpret_castは無条件のキャスト(C言語のキャストと同じで内容保証なし)
- static_castはダウンキャスト用で、使える場合にはこちらを使うべき
- 動的に(実行時に)型チェックするにはdynamic_castを使う(仮想関数とRTTI必須)
static_castとreinterpret_cast
まずstatic_castとreinterpret_castはどちらも型を強制的に変換するものという認識で、違いがよく分かってなかった。
reinterpret_cast は無条件にポインタを変換するのに対し、
static_cast は変換元と先のクラスが親と子の関係でなければコンパイルエラーが発生する、という違いがある:
| class Foo {}; | 
ということで static_cast は用途が制限されている、という違いがある。
言うなれば reinterpret_cast がCのキャストと同じもの。
- void*からの変換にはどちらも使える
- コンパイル時に判定するだけなので、実行時には判定はない
- どちらも変換後の内容が実際に変換先クラスのインスタンスであるかどうかは保証されない
多重継承している場合
Base1 と Base2 を継承した Derived クラスがあったとする。
Derived から Base2 にアップキャストした場合、第1の親クラスじゃないのでポインタが調整される:
| class Base1 { protected: virtual ~Base1(){} int x; }; | 
その状態から Derived* への static_cast を使用することでオフセットを考慮してダウンキャストされ、元のポインタが復元できる:
| Derived* d2 = static_cast<Derived*>(b2); | 
reinterpret_cast では考慮されないので復元はできない(オフセットがずれたまま)。
コンパイルワーニングで警告はしてくれる:
| Derived* d3 = reinterpret_cast<Derived*>(b2); | 
dynamic_cast
dynamic_castもダウンキャスト(親クラスから子クラスへの変換)に使用する
(親子関係じゃない場合はコンパイルエラー)。
実行時に実際に意図したクラス(またはそれを継承したクラス)かどうかを判定して
そうであれば変換し、違ったらnullptrを返す。
実行時型情報(RTTI)を用いるので、仮想関数を持っている必要がある。
多重継承している場合
dynamic_castでBase2*からDerived* が復元できる。
static_castとの違いは、Derived*由来じゃないBase2*の場合にはnullptrが返る:
| Base2* other_b2 = new Base2(); | 
const_cast
const_cast はコンパイル時に const ポインタをconstなしポインタとして解釈するよう指示するだけなので、特に不明点はない。