TechCraft – エンジニアのためのスキルアップメモ

エンジニアのスキルアップを少しでも加速する技術ブログ

型の哲学──静的と動的の間にあるもの 2章

型の哲学──静的と動的の間にあるもの

第2章:C++におけるキャストの基礎

「型変換」は静的型付け言語において避けて通れない操作であり、特にC++では多くのキャスト手法が用意されている。C++を学び始めた人にとって、static_castreinterpret_castconst_castdynamic_cast などの違いに戸惑った経験は少なくないだろう。

この章では、C++という静的型システムの代表格におけるキャストの基本を解説しつつ、「なぜキャストが必要なのか」「キャストが多いことの意味」についても掘り下げていく。


2.1 C言語スタイルのキャストとその危険性

int i = 42;
double d = (double)i;

このように、C言語では型名を括弧で囲んで明示的にキャストできる。このスタイルはC++でも使用可能だが、以下の問題を孕んでいる:

  • reinterpret_cast, static_cast, const_cast, dynamic_cast のいずれが行われるか判別できない
  • 可読性が低く、意図が不明瞭
  • 一見便利だが、無意識に未定義動作を招く危険がある

そのため、C++では目的に応じた明確なキャスト演算子を使用することが推奨される。


2.2 static_cast

float f = 3.14f;
int i = static_cast<int>(f);

static_cast は、以下の用途に適している:

  • 基本型同士の変換(float → int など)
  • ポインタや参照のアップキャスト(派生 → 基底)
  • 明示的な変換による意図の明確化

アップキャストの例

class Animal {};
class Dog : public Animal {};

Dog d;
Animal* a = static_cast<Animal*>(&d);  // OK

ダウンキャストには不向き

Animal* a = new Animal();
Dog* d = static_cast<Dog*>(a);  // NG:未定義動作の可能性

→ 安全でないキャストに見えるなら、dynamic_cast を検討するべき


2.3 dynamic_cast

dynamic_cast は、ランタイム型情報(RTTI)を用いてポインタのダウンキャストを安全に行う。

class Animal {
public:
    virtual ~Animal() {}  // RTTIのために仮想関数が必要
};

class Dog : public Animal {};

Animal* a = new Dog();
Dog* d = dynamic_cast<Dog*>(a);  // OK

特徴

  • キャストが成功すればポインタを返す
  • 失敗すればポインタ版は nullptr、参照版は std::bad_cast をスロー
  • RTTIが有効である必要あり(通常はデフォルトで有効)

非常に便利だが注意点

  • 実行時に型チェックが必要なため、パフォーマンスコストがある
  • ランタイムで失敗する可能性をコードに想定すべき

2.4 const_cast

void print(int* p) {
    *p = 100;
}

void process(const int* p) {
    print(const_cast<int*>(p));  // OK
}

主な用途

  • const修飾子を取り除く
  • const APIしかないが非const操作をしたい場合など

注意点

  • constオブジェクトを書き換えると未定義動作
  • 読み取り専用な設計を意図的に壊すため、極力避けるべき

2.5 reinterpret_cast

int i = 42;
char* p = reinterpret_cast<char*>(&i);

特徴

  • 型を「再解釈」するためのキャスト
  • ポインタ同士、整数との相互変換などが可能
  • 非常に強力だが、極めて危険

使用例(ポインタ→整数)

void* ptr = malloc(16);
uintptr_t raw = reinterpret_cast<uintptr_t>(ptr);

使用例(整数→ポインタ)

uintptr_t addr = 0x7ffe12345678;
int* p = reinterpret_cast<int*>(addr);

注意点

  • アライメント違反型別アクセスの未定義動作を招く可能性
  • ハードウェアアクセスやシリアライゼーションなど、低レベルな用途に限定すべき

2.6 キャストが多い理由とC++らしさ

C++は「安全性」と「性能」の両立を目指した言語である。型チェックを強化しつつ、必要な場面では型の壁を越えて操作できる手段が求められた。

  • static_cast:安全な型変換
  • dynamic_cast:実行時に型を検証する
  • const_cast:限定的に修飾子を外す
  • reinterpret_cast:型チェックを完全に無視する

これらを使い分けることで、開発者に最大限の自由を与えつつ、安全性も担保できるようになっている。逆に言えば、それだけC++は「何でもできてしまう」危険な言語でもある。


2.7 まとめ:キャストの型変換マトリクス

キャスト 用途 安全性 主な使用場面
static_cast 基本型変換、アップキャスト 意図的な数値変換や明示的制御
dynamic_cast 安全なダウンキャスト RTTIによる型検査
const_cast const除去 レガシーAPIなどへの一時対応
reinterpret_cast 型再解釈、ポインタ操作 最低 メモリ操作やハードウェア制御など
Cスタイル 上記すべて(あいまい) 不明 C++では原則非推奨

参考書籍