閉じる

C++のstatic_cast入門|基本文法と安全な使い方まとめ

C++では、型変換は避けて通れないテーマです。

その中でもstatic_castは「コンパイル時にチェックされる」比較的安全なキャストとして、現代的なC++コードでよく使われます。

本記事では、C++のstatic_castの基本文法、使いどころ、注意点をコンパクトに整理し、Cスタイルキャストからの書き換え方までまとめて解説します。

C++におけるキャストの全体像

旧来のキャストとC++スタイルキャスト

C++では、型変換の書き方は大きく分けて2種類あります。

1つはC言語由来の(type)exprのようなCスタイルキャスト、もう1つはC++で導入されたstatic_castdynamic_castなどのC++スタイルキャストです。

Cスタイルキャストは簡潔ですが、実際には複数種類の変換(暗黙変換、const除去、reinterpretなど)を一度に行おうとするため、何が起きているのか分かりにくく、危険なケースも紛れ込みます。

それに対してstatic_castは「できること」が限定されており、コンパイル時にしっかりチェックされるため、日常的な型変換ではまずstatic_castを優先して使うのがおすすめです。

static_castの基本文法

文法と読み方

static_castの基本形は次のようになります。

C++
// basic syntax
static_cast<変換先の型>(式)

例えば、doubleをintに変換する場合は次のように書きます。

C++
#include <iostream>

int main() {
    double d = 3.7;

    // double から int への変換
    int n = static_cast<int>(d);

    std::cout << "d = " << d << std::endl;
    std::cout << "n = " << n << std::endl;  // 少数部分が切り捨てられる

    return 0;
}
実行結果
d = 3.7
n = 3

static_cast<int>(d)は「dをint型として扱う(明示的に変換する)」という意味です。

Cスタイルの(int)dと見た目は似ていますが、許される変換の範囲がはっきり決まっており、コンパイラがより厳しくチェックしてくれます。

どんなときに使えるのか(ざっくり)

static_castは、主に次のような変換に使えます。

  • 整数・浮動小数点など数値型どうしの変換
  • 列挙型と整数との変換(一部制限あり)
  • ポインタ同士の変換(関連のある型同士)
  • クラスの継承関係にあるポインタ・参照の変換(アップキャスト/ダウンキャストのうち安全と判定できる範囲)
  • voidポインタと他のポインタの間の変換

逆にメモリアドレスの再解釈のような危険な変換はできません。

そのような用途はreinterpret_castの領域になります。

数値型の変換でのstatic_cast

整数と浮動小数点の変換

数値型の変換は最もよく使う場面です。

例えば、割り算の結果を浮動小数点で得たい場合に、static_castで明示的にdoubleに変換します。

C++
#include <iostream>

int main() {
    int a = 3;
    int b = 2;

    // 整数同士の割り算 → 小数部分が切り捨てられる
    int i_result = a / b;

    // 片方をdoubleに変換してから割り算 → 浮動小数点の結果になる
    double d_result = static_cast<double>(a) / b;

    std::cout << "a / b (int)    = " << i_result << std::endl;
    std::cout << "a / b (double) = " << d_result << std::endl;

    return 0;
}
実行結果
a / b (int)    = 1
a / b (double) = 1.5

片方をstatic_castで浮動小数点に変換すると、演算全体が浮動小数点として評価されることがわかります。

桁あふれや情報損失に注意

static_castはコンパイル時に変換の妥当性をチェックしますが、値の範囲がオーバーするかどうかまでは実行時に保証してくれません

C++
#include <iostream>
#include <limits>

int main() {
    long long big = std::numeric_limits<long long>::max();

    // long long から int へ変換 (値が入りきらない可能性)
    int n = static_cast<int>(big);

    std::cout << "big = " << big << std::endl;
    std::cout << "n   = " << n   << std::endl; // 実装依存の結果になる可能性

    return 0;
}
実行結果
big = 9223372036854775807
n   = 2147483647   // など (処理系による)

コンパイルは通りますが、実行時に意味のある値が得られる保証はありません

範囲外の値の変換では、事前にチェックする、あるいはより安全な変換関数(ライブラリ)を利用することが重要です。

列挙型と整数の変換

列挙値から整数へ

C++の列挙型は内部的に整数値を持ちますが、それを取り出すにはstatic_castを使うのが定番です。

C++
#include <iostream>

enum Color {
    Red = 1,
    Green = 2,
    Blue = 4
};

int main() {
    Color c = Green;

    // 列挙値をintに変換
    int value = static_cast<int>(c);

    std::cout << "Color::Green = " << value << std::endl;

    return 0;
}
実行結果
Color::Green = 2

列挙型から整数への変換はstatic_castで明示することで、コードを読む人に「ここで整数値が欲しいのだな」と意図が伝わります。

整数から列挙値へ(注意点)

逆に整数から列挙値へ変換することもできますが、意味のある値かどうかはコンパイラには分かりません

C++
#include <iostream>

enum class Status {
    Ok = 0,
    Error = 1,
    Unknown = 2
};

int main() {
    int code = 5;

    // int から enum class への変換
    Status s = static_cast<Status>(code);

    // s は Status::Ok / Error / Unknown のいずれでもない値になり得る
    std::cout << "code = " << code << std::endl;

    return 0;
}
実行結果
code = 5

このようにenum classへのstatic_castは可能だが、安全性のチェックは呼び出し側の責任です。

普通はswitchなどで妥当な範囲かどうかを検査してから使います。

ポインタと参照でのstatic_cast

上位クラス(基底クラス)へのポインタ変換(アップキャスト)

オブジェクト指向プログラミングでは、派生クラスのポインタを基底クラスのポインタに変換することがよくあります。

これは安全な変換であり、static_castで行えます

C++
#include <iostream>

class Base {
public:
    virtual void speak() const {
        std::cout << "Base::speak()" << std::endl;
    }
};

class Derived : public Base {
public:
    void speak() const override {
        std::cout << "Derived::speak()" << std::endl;
    }
};

int main() {
    Derived d;

    // Derived* から Base* へのアップキャスト
    Base* b = static_cast<Base*>(&d);

    b->speak();  // 仮想関数により Derived::speak() が呼ばれる

    return 0;
}
実行結果
Derived::speak()

アップキャストは暗黙変換でも行えますが、あえてstatic_castを使うことで「ここで意図的に基底クラスとして扱う」ことを明示できます。

下位クラス(派生クラス)への変換(ダウンキャスト)

逆に、基底クラスのポインタから派生クラスのポインタへ変換することをダウンキャストといいます。

これは実行時に本当にその型であるかどうか分からないため、通常はdynamic_castを使います。

static_castでもダウンキャストは書けてしまいますが、実行時チェックが行われないため危険です。

C++
#include <iostream>

class Base {
public:
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    void hello() const {
        std::cout << "Hello from Derived" << std::endl;
    }
};

int main() {
    Base base;
    Base* pb = &base;

    // 危険なダウンキャストの例 (実行時チェックなし)
    Derived* pd = static_cast<Derived*>(pb);

    // 未定義動作になる可能性が高い (本当はDerivedではない)
    // pd->hello();  // 実際のコードでは避けるべき

    std::cout << "static_cast によるダウンキャストは危険です" << std::endl;

    return 0;
}
実行結果
static_cast によるダウンキャストは危険です

ダウンキャストが必要なときはdynamic_castを優先し、static_castを使うのは「型が必ず一致する」と分かっている特別な状況に限るべきです。

voidポインタとの変換

void* から特定の型へのキャスト

C言語的なAPIではvoid*を使うことがあります。

このとき、元の型が明らかな場合にはstatic_castで元の型へ戻します。

C++
#include <iostream>

void printInt(void* ptr) {
    // void* から int* へキャスト
    int* p = static_cast<int*>(ptr);
    std::cout << "value = " << *p << std::endl;
}

int main() {
    int x = 42;
    void* v = &x;

    printInt(v);

    return 0;
}
実行結果
value = 42

void* は「型不明のポインタ」なので、その実体が何であるかを呼び出し側が保証する必要があります。

型が食い違うと未定義動作になるため、実務コードではなるべくvoid*を避け、テンプレートや型安全なラッパーで置き換えることが多いです。

Cスタイルキャストとの違いと置き換え方

Cスタイルキャストの問題点

Cスタイルキャスト(T)exprは、次のように「複数のキャストの組み合わせ」として解釈されます。

  • const_cast 相当
  • static_cast 相当
  • reinterpret_cast 相当
  • それらの一部の組み合わせ

一見シンプルに見えますが、実際には「何が起きているか分かりにくく、危険な変換も紛れ込む」という問題があります。

static_castへの書き換えの基本パターン

Cスタイルキャストの多くは、static_castへ書き換えることができます。

例として、数値型変換やアップキャストの場合を見てみます。

C++
#include <iostream>

class Base {};
class Derived : public Base {};

int main() {
    double d = 3.14;

    // Cスタイルキャスト
    int n1 = (int)d;

    // static_cast への書き換え
    int n2 = static_cast<int>(d);

    Derived der;
    Derived* pd = &der;

    // Cスタイルキャストによるアップキャスト
    Base* b1 = (Base*)pd;

    // static_cast への書き換え
    Base* b2 = static_cast<Base*>(pd);

    std::cout << "n1 = " << n1 << ", n2 = " << n2 << std::endl;
    std::cout << "b1, b2 はどちらも Base* になる" << std::endl;

    return 0;
}
実行結果
n1 = 3, n2 = 3
b1, b2 はどちらも Base* になる

「Cスタイルキャストで本当にやりたいこと」がstatic_castの範囲に収まるなら、static_castに置き換えることで、意図が明確で安全性も高まります。

逆に、constを外したい場合はconst_cast、メモリ表現を再解釈したい場合はreinterpret_castを使うべきで、「なんでもかんでもstatic_castで済ませる」ことも避ける必要があります。

static_castを安全に使うためのコツ

「暗黙変換」と「明示的変換」の切り分け

C++では、暗黙に型変換が行われる場面が多くありますが、情報損失や意味の変化がある変換はstatic_castで明示すると読みやすいコードになります。

  • 安全で自明な変換
    例: int → long long など
    → 暗黙変換のままでも良い場合が多い
  • 情報が失われうる変換
    例: double → int, long long → int, enum class → int など
    → static_castで「ここであえて変換している」と示す

キャストそのものを減らす設計も重要

static_castを正しく使うのも大切ですが、そもそもキャストの必要がない設計の方が望ましい場合も多くあります。

  • APIの引数型を適切に設計する
  • テンプレートを使って型に依存しない関数にする
  • enum classを使って暗黙変換を防ぐ

など、「キャストが必要になる状況を減らす」ことも併せて意識すると、より安全で保守しやすいコードになります。

まとめ

static_castは、C++における「比較的安全で、意図を明確に示せる型変換の手段」です。

数値型同士の変換や列挙型との変換、ポインタのアップキャストやvoid*との往復など、日常的な場面の多くをstatic_castでカバーできます。

一方で、ダウンキャストや範囲外の数値変換など、static_castだけでは安全が保証されないケースも存在します。

そのようなときはdynamic_castや事前チェックなどを組み合わせることが大切です。

Cスタイルキャストを安易に使うのではなく、まずstatic_castを候補に挙げ、「なぜここで変換が必要なのか」を意識しながらコードを書くことで、読みやすく安全なC++プログラムに近づくことができます。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!