型変換
![]() |
型変換(かたへんかん、英: type conversion)とはプログラムにおいて、あるデータ型を他のデータ型に変換することである[1]。型キャスト(英: type casting)とも呼ばれる[2]。
目次
分類[編集]
暗黙の型変換と明示的型変換[編集]
暗黙の型変換は、明示的に指定しなくてもコンパイラの判断によって自動的に行われる型変換である。逆に、明示的に指定して行う型変換を明示的型変換という。
暗黙の型変換では、たとえばある式の中に複数の型の変数がある場合、すべての変数を最も上位の型に変換する。
double d;
long l;
int i;
/* ... */
if (d > i) d = i;
if (i > l) l = i;
if (d == l) d *= 2;
このC言語のコードでは、d
とl
とi
は異なったデータ型をもっているので、すべての演算は自動的に同じ型に変換された後に行われる。より大きなサイズの型に変換されることを拡大変換 (widening conversion) と呼び、より小さなサイズの型に変換されることを縮小変換 (narrowing conversion) と呼ぶ。
暗黙の型変換には注意しなければならないこともある。たとえばdouble
型(浮動小数点数)の値をint
型(整数型)の変数に代入する場合、小数点以下の端数があったり、元の値がint
型で表現できる範囲を超えていたりすると、縮小変換により情報の一部が失われてしまう。C言語およびC++では暗黙の縮小変換を許しているが、情報が失われる可能性のある変換に対して、通例コンパイラが警告を出す。JavaやC#などの後発言語では、暗黙の縮小変換を許可せず、後述するキャスト (cast) 構文による明示的な変換が必要となる。
組込みの型変換とユーザ定義の型変換[編集]
基本的な型変換(整数どうしの変換や、整数と浮動小数点数との間の変換など)は、多くの処理系でコンパイラまたはプロセッサ内に既に定義されている。一方、ある型から別の型への変換をユーザが定義できる言語もある。
例えばC++では、ユーザ定義型の中に変換元の型を一つだけとるコンストラクタを定義すれば、ユーザ定義の暗黙の型変換が定義できる。コンストラクタにexplicit修飾子をつけると暗黙の型変換が許されなくなり、明示的型変換が必要となる。
class Class1 { };
class Class2 {
public:
explicit Class2(Class1 c1) { ... }
};
void test() {
Class1 c1;
Class2 c2 = (Class2)c1;
// explicit修飾子がなければ Class2 c2 = c1; でよい
}
ここで、Class1とClass2の間には継承関係がないにもかかわらず代入ができている。これはClass1からコンストラクタを通してClass2に型変換されるからである。
なお、上の例では型変換の構文をとってはいるが、実際の処理としてはc1
はコンストラクタへの引数として渡されている。そのため、本来必要のないc1
のコピーが生成される。これを避けるために、変換元の型がユーザ定義型である場合には、通常は引数を参照として渡す。また、型変換という操作の意味を考えれば、変換元のインスタンスに変更を加えるということはあり得ないので、通常は引数にconst
修飾子をつけて変更不可とする。結局、コンストラクタの宣言はexplicit Class2(const Class1& c1) { ... }
のように書くことが多い。
キャストとその分類[編集]
C言語とその流れにある言語では、キャスト演算子によるキャスト式により、式の右辺値を指定された型に型変換する。この構文をキャストと呼ぶ。C言語のキャスト演算子は、型名を括弧で囲んだ形式 (Type)
であり、目的の式に前置する。
double d = 1234.5678;
int x = (int)d;
C++では従来のC言語形式のキャスト構文のほか、用途および意味を明確にした4つの異なるキャスト構文(static_cast
, reinterpret_cast
, const_cast
, dynamic_cast
)が用意されている。C++では意味が曖昧なC言語形式のキャスト構文は推奨されず、状況に応じて4つのキャスト構文を使い分けることが推奨される。
アップキャスト[編集]
あるクラスBaseと、Baseから派生したクラスDerivedがあるとする。アップキャストとは、派生クラスから基底クラスへの型変換、すなわちDerivedのインスタンスをBaseに変換する操作である。「DerivedのインスタンスはBaseのインスタンスである」ことは保証されているので、一般的にはこの変換は安全である。そのため、多くの言語において、これは暗黙に行うことができる。
ただし、C++において多重継承のクラスのアップキャストが安全でないとされる環境[要出典]では、dynamic_castを使うことが推奨される[要出典]。
ダウンキャスト[編集]
ダウンキャストはアップキャストの逆で、基底クラスから派生クラスへの型変換、すなわちBaseのインスタンスをDerivedに変換する操作である。Baseのインスタンスは必ずしもDerivedのインスタンスとは限らないので、この変換は一般に安全ではなく、エラーが発生する可能性がある。そのため、多くの言語ではキャスト構文による明示的な変換の記述が必要である。通例、オブジェクト指向プログラミングではポリモーフィズムを使うべきであり、ダウンキャストおよびクロスキャストが必要になるということはプログラムの設計に問題があることを示唆している。
C++では、安全なダウンキャストのためにdynamic_cast
という特別な構文が用意されている。この構文では、実行時型情報を参照し、ポインタ間の変換が失敗すると結果としてNULLが返る。参照間の変換が失敗するとstd::bad_cast
例外がスローされる。dynamic_cast
を使用するためには、型に仮想関数テーブルが必要となる。つまり、基底クラスに少なくとも1つの仮想関数を持つ必要がある。
Javaでは、ダウンキャストに失敗するとjava.lang.ClassCastException
例外がスローされる。C++のdynamic_cast
に相当する機能は存在しないが、instanceof
演算子で型情報を問い合わせることはできる。
C#では、ダウンキャストに失敗するとSystem.InvalidCastException
例外がスローされる。また、as
演算子が用意されており、変換が失敗した場合はnullが返る
[3]。
またC# 7.0では、is
演算子が拡張され、変換可能性をbool型で返すと同時に、末尾で宣言した変数に変換結果が格納される
[4]。
as
演算子is
演算子共に、通常のキャスト演算子とは異なり、ユーザ定義変換は行われない。
クロスキャスト[編集]
あるクラスDerivedが、二つの基底クラスBase1とBase2を多重継承しているとする。このとき、例えばBase1からBase2のように基底クラスどうしの間で型変換することをクロスキャストという。変換する対象がDerivedのインスタンスであればキャストは成功するが、それは実行時にならないと分からないので、ダウンキャストと同様に安全な型変換ではない。
C++では、ダウンキャストと同じ構文dynamic_cast
で安全なクロスキャストが行える。
静的キャスト[編集]
整数どうしの型変換や整数と浮動小数点数との間の型変換などの、ごく一般的な型変換。内部的には64bitで表すデータを32bitなどに変換する縮小変換や、32bitで表すデータを64bitなどに変換する拡大変換を伴う場合もある。一例としては、JavaやC#のint
からlong
への型変換はサイズ長を倍化させる拡大変換であり、逆にlong
からint
への型変換はサイズ長を半減させる縮小変換である。