traPアドベントカレンダー2019の記事です。
書くことないので軽率にC++の話をします。
想定読者
CのノリでC++使ってる人
C++勉強中ですって感じの人
非想定読者
ワタシ C++ チョットデキル
本題
Cでのキャストと言ったらこういうのですよね。C++でもこれは使えます。
double b = 2.5;
int a = (int)b;
でも実は…C++にはもっと違う素晴らしいキャストの方法があるんです!C++ではこれを使っていきましょう!
int a = static_cast<int>(b);
は????
長い。打つのめんどくさい。なぜこんなことをしなければならないのか?
なぜならキャストは罪悪だからです。人は罪の自覚を持つことで救済への道が開かれます。
古の方法では様々な横暴が許されます。
int a;
double* b = (double*)&a;
int* c = (int*)(0x00001000);
const int d = 100;
c = (int*)&d;
MSVCで実験しましたが、このコードはコンパイルが通りました。
ただの整数をポインタ型にすることもできるなら、const外しもできますし、全く違う型同士のポインタの変換もできます。
流石にこれは極端な例ですが、まともなキャストもこのような極端なキャストも同じく只のカッコで実現できてしまうということは、極端なキャストを自覚なく行ってしまったり、探しにくくなる可能性があるということです。
また、キャストはおなじみのカッコ形式のものに限りません。暗黙のキャストというものも存在します。
long long a = 100000000000000LL;
int b = a;
void* p = &a;
int* c = (int*)p;
これらのコードも通ります。
小さい整数型への暗黙キャストなどにコンパイラは警告を出しますが愚かな人類はそれを無視し、やがて桁あふれに気付かず滅びたりします。
キャストの恐ろしさは理解できましたでしょうか?
このような闇に光を灯すため、C++では以下のような4つのキャスト演算子が作られました。
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
総じて名前が長い!
この長い名前をタイプすることで人々は罪の自覚へと誘われます。額に7つのPは見えましたか?
これらの使い方は簡単です。****_cast<キャスト後の型>(キャストする値)
とこのように書くだけです。テンプレート関数みたいなノリで書くと良いでしょう。
double b = 2.5;
int a = static_cast<int>(b);
4種類あるC++スタイルのキャストですが、基本的にはstatic_castのみを使います。他はほぼ使いませんというか出来る限り使わないべきです。
static_cast
整数型→サイズの違う整数型、整数型↔浮動小数点型、ポインタ型↔void*型などの変換を許可してくれます。
暗黙的なキャストで通るもの、及びvoid*型→ポインタ型のキャストを通してくれます。
これを付けることにより警告は消え、人々は不可解なバグが出たときにあの時タイプしたstatic_castを脳裏に浮かべながら神への懺悔とともにCtrl+Fを行うことなどができます。
const_cast
constを付けたり外したりすることができるcastです。
正直要らない子
外部ライブラリでconstの外れた型の引数を欲しがる困ったちゃんな関数がいたりとかすると使えるのかも…
あるいは存在そのものが我々に罪の自覚を促しているのかもしれません。
dynamic_cast
Cスタイルの括弧キャストでは代替できない異端児です。親クラスのポインターを子クラスのポインターにキャストすることができます。
このキャストの異端児たるゆえんは「失敗」することがありうるということ。例えば以下のような場合です。
class A { ... };
class B : A { ... };
class C : A { ... };
B hoge;
A* foo = static_cast<A*>(&hoge);
C* = dynamic_cast<C*>(foo);
変換に失敗した場合は例外が飛びます。
覚悟(try-catch節)を持つものにのみ許されたキャストです。
reinterpret_cast
直訳すれば「再解釈キャスト」であり、最も罪深きキャストです。その名前の長さとタイプ量が罪の大きさを表しています。
このキャストはポインターのあらゆる整合性をぶっちぎります。
異なる型のポインター間の変換は朝飯前、生アドレスの整数をポインター型にすることも可能です。
これをたくさん書くととても背徳感があります。
しかしこのreinterpret_castも、int型とlong long型のようなビットサイズの違う型同士の変換ではエラーが出ます。罪には種類があるということなのでしょうね。
以上CスタイルのキャストはC++において4種のキャストへと分かたれました。これがバベるということです。使い分けていきましょう。
まとめ
型と和解せよ
明日はAdwaver_4157さんの記事です。