第1章:C++の型変換
はじめに
CPP Module 06では、C++の型変換(Cast)を学びます。C言語のキャストは柔軟ですが危険でもあります。C++は4種類の明示的キャストを導入し、意図を明確にしながら型安全性を向上させました。
---
1. C言語のキャスト
1.1 C言語のキャスト構文
int i = 42;
double d = (double)i; // 明示的変換
void* ptr = &i;
int* ip = (int*)ptr; // ポインタ変換
const int ci = 42;
int* mutable_ptr = (int*)&ci; // const除去(危険)
1.2 問題点
C言語のキャストは以下の問題があります:
- 意図が不明確: 何をしたいのか分からない
- 検索困難:
(type)パターンは検索しにくい - 安全性の欠如: あらゆる変換を許可
- エラー検出困難: 間違いがコンパイル時に検出されにくい
---
2. C++の4種類のキャスト
2.1 概要
| キャスト | 用途 |
|---------|------|
| static_cast | 暗黙的変換の逆、明確な型変換 |
| dynamic_cast | 実行時の安全な派生クラスへの変換 |
| const_cast | const/volatile修飾子の除去 |
| reinterpret_cast | ビットパターンの再解釈 |
2.2 構文
static_cast<target_type>(expression)
dynamic_cast<target_type>(expression)
const_cast<target_type>(expression)
reinterpret_cast<target_type>(expression)
---
3. static_cast
3.1 用途
static_castはコンパイル時に型チェックを行う安全なキャストです:
// 数値変換
int i = 42;
double d = static_cast<double>(i);
int back = static_cast<int>(d);
// enumと整数
enum Color { RED, GREEN, BLUE };
int colorNum = static_cast<int>(RED);
Color c = static_cast<Color>(2);
// ポインタ変換(関連する型のみ)
class Base {};
class Derived : public Base {};
Base* b = new Derived();
Derived* d = static_cast<Derived*>(b); // 安全性は保証されない
3.2 static_castの制限
// 許可されない変換
int i = 42;
char* c = static_cast<char*>(&i); // エラー!関連のない型
const int ci = 42;
int* p = static_cast<int*>(&ci); // エラー!const除去不可
3.3 暗黙的変換の逆
// 暗黙的変換
int i = 3.14; // double → int(警告が出る場合あり)
// static_castで明示的に
int i = static_cast<int>(3.14); // 意図を明確に
---
4. dynamic_cast
4.1 用途
dynamic_castは実行時に型チェックを行い、安全に派生クラスへダウンキャストします:
class Base {
public:
virtual ~Base() {} // 仮想関数が必要
};
class Derived : public Base {};
void process(Base* b) {
Derived* d = dynamic_cast<Derived*>(b);
if (d) {
// 安全にDerivedとして使用
} else {
// 変換失敗
}
}
4.2 参照とdynamic_cast
void process(Base& b) {
try {
Derived& d = dynamic_cast<Derived&>(b);
// 成功
} catch (std::bad_cast& e) {
// 失敗
}
}
4.3 要件
dynamic_castを使用するには:
- 基底クラスに少なくとも1つの仮想関数が必要
- RTTI(Run-Time Type Information)が有効
4.4 RTTI
#include <typeinfo>
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {};
void identify(Base* b) {
std::cout << typeid(*b).name() << std::endl;
}
---
5. const_cast
5.1 用途
const_castはconstやvolatile修飾子を除去します:
void legacyFunction(char* str); // constを取らない古いAPI
void modernFunction(const char* str) {
legacyFunction(const_cast<char*>(str));
}
5.2 危険性
const int ci = 42;
int* p = const_cast<int*>(&ci);
*p = 100; // 未定義動作!元がconstの場合
// 元がnon-constなら安全
int i = 42;
const int* cp = &i;
int* p2 = const_cast<int*>(cp);
*p2 = 100; // OK
5.3 ベストプラクティス
const_castは以下の場合のみ使用:
- レガシーAPIとの互換性
- オーバーロード解決
- 元がnon-constであることが保証される場合
---
6. reinterpret_cast
6.1 用途
reinterpret_castはビットパターンを再解釈します:
// ポインタを整数に変換
int i = 42;
uintptr_t ptr_val = reinterpret_cast<uintptr_t>(&i);
// 整数をポインタに変換
int* p = reinterpret_cast<int*>(ptr_val);
// 関数ポインタの変換
typedef void (*FuncPtr)();
FuncPtr fp = reinterpret_cast<FuncPtr>(0x12345678);
6.2 シリアライズでの使用
struct Data {
int x, y;
};
// シリアライズ
uintptr_t serialize(Data* ptr) {
return reinterpret_cast<uintptr_t>(ptr);
}
// デシリアライズ
Data* deserialize(uintptr_t raw) {
return reinterpret_cast<Data*>(raw);
}
6.3 危険性
reinterpret_castは最も危険なキャストです:
- 型の整合性を保証しない
- アラインメントの問題を引き起こす可能性
- ポータビリティの問題
---
7. スカラー型の変換
7.1 スカラー型とは
スカラー型は単一の値を持つ型です:
charintfloatdouble
7.2 リテラルの判定
42の課題では、文字列リテラルを解析して適切な型に変換します:
// 特殊リテラル
"-inff", "+inff", "nanf" // float
"-inf", "+inf", "nan" // double
// 文字リテラル
'a', '*' // char(表示可能)
'\n' // char(非表示)
// 整数リテラル
"42", "-123", "0"
// 浮動小数点リテラル
"42.0f", "3.14f" // float
"42.0", "3.14" // double
7.3 変換フロー
void convert(const std::string& literal) {
// 1. 特殊値のチェック
// 2. 単一文字のチェック
// 3. 数値の解析
// 各型への変換と表示
printChar(value);
printInt(value);
printFloat(value);
printDouble(value);
}
---
8. 42課題での実装ポイント
8.1 ex00: Conversion of scalar types
class ScalarConverter {
public:
static void convert(const std::string& literal);
private:
ScalarConverter();
ScalarConverter(const ScalarConverter& other);
ScalarConverter& operator=(const ScalarConverter& other);
~ScalarConverter();
};
8.2 ex01: Serialization
uintptr_t serialize(Data* ptr);
Data* deserialize(uintptr_t raw);
8.3 ex02: Identify real type
class Base {
public:
virtual ~Base();
};
class A : public Base {};
class B : public Base {};
class C : public Base {};
Base* generate();
void identify(Base* p);
void identify(Base& p);
---
9. キャストの選択ガイド
型変換が必要
│
├─ 数値変換? ─────────────> static_cast
│
├─ ポインタ/参照の継承関係?
│ │
│ ├─ ダウンキャスト? ──> dynamic_cast
│ │
│ └─ アップキャスト? ──> 暗黙的変換 or static_cast
│
├─ const/volatile除去? ──> const_cast
│
└─ ビット再解釈? ────────> reinterpret_cast
---
まとめ
本章で学んだこと:
次章では、各演習の詳細な実装を解説します。