型キャスト

数値型のキャスト

数値型を別の数値型に変換する必要性はよく発生します。全ての数値型の変換が可能なわけではありません。許可されたキャストのスキームは次の通りです。

可能な型キャストのスキーム

黒い矢印は、キャストがほとんど情報を失うことなく行われることを示します。char 型の代わりにbool 型(共に 1 バイト)、int 型の代わりに color 型(4 バイト)、long 型の代わりに datetime 型(8 バイト)も使用出来ます。4 本の灰色の破線の矢印は、キャストで精度の損失が発生する可能性があることを示します。例えば、123,456,789(int)に等しい整数の桁数は float で表すことが出来る桁数よりも大きいです。

  int n=123456789;
  float f=n;   // f の中身は 1.234567892E8 に等しい
  Print("n = ",n,"   f = ",f);
  // 結果 n= 123456789    f= 123456792.00000

float に変換された数は同じ位数を持っていますが、正確差が劣ります。黒矢印と反対方向の変換は、データの損失を伴う可能性がありますが行うことが出来ます。char と uchar、short と ushort、int と uint、long と ulong の間の変換(両方向)は、データの損失につながる可能性があります。

浮動小数点値を整数型に変換した結果、小数部分は常に削除されます。float を最も近い整数に四捨五入する場合には(多くの場合、実用性がより高いため)、MathRound() が使用されるべきです。

例:

//--- 重力加速度
  double g=9.8;
  double round_g=(int)g;
  double math_round_g=MathRound(g);
  Print("round_g = ",round_g);
  Print("math_round_g = ",math_round_g);
/*
  結果:
  round_g = 9
  math_round_g = 10
*/

2 つの値が 2 進数演算子で結合される場合は、演算が実行される前に、以下のスキームに与えられた優先度に応じて、低型のオペランドがより高型に変換されます。

2 進数演算子によるリンク時のキャスト

データ型 char、uchar、short 及び ushort は無条件で int 型に変換されます。

例:

  char   c1=3;
//--- 例1
  double d2=c1/2+0.3;
  Print("c1/2 + 0.3 = ",d2);
// 結果:   c1/2+0.3 = 1.3
 
//--- 例 2
  d2=c1/2.0+0.3;
  Print("c1/2.0 + 0.3 = ",d2);
// 結果:   c1/2.0+0.3 = 1.8

計算された式は、2 つの演算から構成されています。例1では、除算演算で第 2 オペランドである定数 2 がより高い int 型であるため、char 型の変数 c1 は int 型の一時変数に変換されます。3/2 の整数除算の結果、int 型の値 1 が取得されます。

例1の 2 番目の演算では、第 2 オペランドが double 型定数 0.3 であるため、1 番目の演算の結果は、1.0 の値を持つ double 型の一時変数に変換されます。

例 2 では、除算演算で第 2 オペランドである定数 2.0 が double 型であるため、char 型変数の c1 は、double 型の一時変数に変換されます。更なる変換は行われません。

 

数値型の型キャスト

MQL5 言語の式では、明示的及び暗黙的な型キャストの両方を使用することが出来ます。明示的な型キャストは次のように書かれます:

var_1 = (型)var_2;

式または関数の実行結果が var_2 変数として使用出来ます。明示的な型キャストの関数スタイルの記法も可能です。

var_1 = 型(var_2);

例1に基づいて明示的な型キャストを考えてみましょう。

//--- 例 3
  double d2=(double)c1/2+0.3;
  Print("(double)c1/2 + 0.3 = ",d2);
// 結果:   (double)c1/2+0.3 = 1.80000000

除算演算が実行される前に、変数 c1 は、明示的に double 型にキャストされます。最初のオペランドが double 型変換にされた結果として、整数定数 2 が double 型の値 2.0 にキャストされます。実際には、明示的な型キャストは単項演算です。

また、型キャストしようとした時に、結果が許容範囲を越える可能性があります。この場合には、切り捨てが発生します。例えば、

  char c;
  uchar u;
  c=400;
  u=400;
  Print("c = ",c); // 結果 c=-112
  Print("u = ",u); // 結果 u=144

(代入以外の)演算が実行される前に、データが最優先タイプに変換されます。代入演算が実行される前に、データは、ターゲットの型にキャストされます。

例:

  int    i=1/2;       // 型キャストなし。結果は 0
  Print("i = 1/2  ",i);
 
  int k=1/2.0;         // 式が double にキャストされ
  Print("k = 1/2  ",k); // そしてターゲット型の int にキャストされる。結果は 0
 
  double d=1.0/2.0;   // 型キャストなし。結果は 0.5
  Print("d = 1/2.0; ",d);
 
  double e=1/2.0;     // 式が double にキャストされ
  Print("e = 1/2.0; ",e);// それがターゲット型と同じ。結果は 0.5
 
  double x=1/2;       // int 型の式がターゲットの double 型にキャストされる
  Print("x = 1/2; ",x); // 結果は 0.0

long/ulong 型を double に変換する場合、整数が 9,223,372,036,854,774,784 より大きく -9,223,372,036,854,774,784 未満の場合、精度が失われることがあります。

void OnStart()
 {
  long l_max=LONG_MAX;
  long l_min=LONG_MIN+1;
//--- double にキャストされた時に正確さを失わない最大の整数値を定義する
  while(l_max!=long((double)l_max))
     l_max--;
//--- double にキャストされた時に正確さを失わない最小の整数値を定義する
  while(l_min!=long((double)l_min))
     l_min++;
//--- 見つかった整数値の間隔を導出する  
  PrintFormat("When casting an integer value to double, it must be "
              "within [%I64d, %I64d] interval",l_min,l_max);
//--- 値がこの間隔外にある場合どうなるか見てみる
  PrintFormat("l_max+1=%I64d, double(l_max+1)=%.f, ulong(double(l_max+1))=%I64d",
              l_max+1,double(l_max+1),long(double(l_max+1)));
  PrintFormat("l_min-1=%I64d, double(l_min-1)=%.f, ulong(double(l_min-1))=%I64d",
              l_min-1,double(l_min-1),long(double(l_min-1)));
//--- 次の結果を受け取る
// 数値を double にキャストする場合 [-9223372036854774784, 9223372036854774784] 間隔でなければいけない。
// l_max+1=9223372036854774785, double(l_max+1)=9223372036854774800, ulong(double(l_max+1))=9223372036854774784
// l_min-1=-9223372036854774785, double(l_min-1)=-9223372036854774800, ulong(double(l_min-1))=-9223372036854774784
 }

 

文字列型の型キャスト

文字列型は、基本データ型の中で最も高い優先順位を有します。そのために、演算のオペランドの 1 つが文字列型である場合には2 つ目のオペランドは自動的に文字列にキャストされます。文字列のために、また、単一の二項2位の動作が可能であることにご注意ください。文字列の任意の数値型への明示的なキャストは許可されています。

例:

  string s1=1.0/8;             // 式が double 型にキャストされてから、
  Print("s1 = 1.0/8; ",s1);     //  ターゲットの string 型にキャストされる
// 結果は "0.12500000" (10 文字を含む文字列)
 
  string s2=NULL;               // 文字列の初期化
  Print("s2 = NULL; ",s2);     // 結果は空の文字列
  string s3="Ticket N"+12345;   // 式が string 型にキャストされる
  Print("s3 = \"Ticket N\"+12345",s3);
 
  string str1="true";
  string str2="0,255,0";
  string str3="2009.06.01";
  string str4="1.2345e2";
  Print(bool(str1));
  Print(color(str2));
  Print(datetime(str3));
  Print(double(str4));

 

基本クラスのポインタから派生クラスのポインタへの型キャスト

オープン生成されたクラスオブジェクトは、対応する基本クラスのオブジェクトと見なすことが出来ます。これは、いくつかの興味深い結果をもたらします。例えば、単一の基本クラスによって生成された異なるクラスのオブジェクトは、互いに大きく異なる可能性があるという事実にもかかわらず、両クラスが基本型のオブジェクトとしてみなされるために、それらのリンクリスト(リスト)を作成することが出来ます。しかし、逆は真ではありません。基本クラスのオブジェクトは自動的に派生クラスのオブジェクトにはなりません。

基本クラスのポインタを派生クラスのポインタに変換するためには、明示的なキャストが使用されます。しかし、このようなキャストは間違えなしに行う必要があります。さもないと深刻な実行時エラーが発生し MQL5 プログラムが停止されます。

dynamic_cast演算子を使用した型の動的動作 #

クラスのポインタを適用することができるdynamic_cast演算子を使った型の動的キャストがあります。このタイプの精度の検証は、実行時に行われます。これはつまり、dynamic_cast演算子の使用時に、コンパイラは使用するデータの型の検証を行わないということを意味します。もし、実際のタイプではないオブジェクトのデータ型へのポインタの変換がある場合、結果はNULLとなります。

dynamic_cast <type-id> ( expression )

角括弧の中のパラメータ type-id は、以前に定義されたクラスのタイプのポインタでなければなりません。オペランドタイプexpressionは(C++とは異なり)void以外の任意のものであっても良いです。

例:

class CBar { };
class CFoo : public CBar { };
 
void OnStart()
 {
  CBar bar;    
//--- *barの*fooへのポインタタイプの動的キャストは許可されました
  CFoo *foo = dynamic_cast<CFoo *>(&bar); // 重大な実行エラーは発生しません 
  Print(foo);                             // foo=NULL      
//--- Bar型オブジェクトのFoo型オブジェクトへの明示的参照のキャストの試みは禁止されています
  foo=(CFoo *)&bar;                       // 重大な実行エラーが発生します
  Print(foo);                             // この行は実行されません
 }

参照

データ型