2010/7/15 - 2011/7/28
Pen Jr.D言語なんてやってみそWelcome Beginner型と変数
<<・・型と変数
・・>>
型( Types
型 というのは 値 の データ形式 を 分類・定義 です。

詳細で厳格な分類は必ずしも必要ではありませんが、型 の 整合性 を厳格に検証することで 記述ミス や 設計ミス を コンパイラ で 発見しやすくなり、より 信頼性 の高いプログラムを組むための助けにもなります。

基本データ型( Basic Data Types )
言語で定義された 型 で、全て 予約語 で、その名の通り 基本要素 の データ形式 で、その他の型の 基 になります。

また、C言語 などとよく似た分類ですが、詳細は異なります。

整数型
桁数と符号の有無で分類・定義されています。
符号付き符号なしサイズ
byteubyte8bit
shortushort16bit
intuint32bit
longulong64bit
centucent128bit将来のため予約

整数値 を扱うほとんどのケースで 32bitint または uint が適切な選択で、明確 な必要性があるとき以外に他の選択をする意味もほとんどありません。

浮動小数点数型
浮動小数点数サイズ虚数型複素数
float32bitifloatcfloat
double64bitidoublecdouble
real最大桁数irealcreal
浮動小数点数 は CPU の扱いも別物で、整数型 に比べ、値の保持や計算に 余計なコスト がかかりますから、必要な時以外は int などの 整数型 を使います。

整数型 の計算では 小数点が切り捨てられますから、

import std.stdio;

void main(){
  // 整数値の計算
  int i = 19;
  float f = 19;
  writeln( i / 4 );
  writeln( i % 4 );   // 余り
  // 浮動小数点の計算
  writeln( f / 4 );
  writeln( f % 4 );
}
D:\Pen-Jr\My Programs\types>test
4
3
4.75
3

小数点を扱うには 浮動小数点数型 の明示が必要です。

小数点数 は一般的ですが、浮動小数点数型 を必要とする 処理 は、数学的な処理 を除くとほとんどなく、また、数学的な処理 を必要とすることも限られますから、浮動小数点型 が必要になるケースも限られます。
文字型
文字コード (符号なしの整数値)を持つ 型 です。
文字コード
char8bitUTF-8
wchar16bitUTF-16
dchar32bitUTF-32

文字コードも整数値ですが、

import std.stdio;

void main(){
  /*
  ^   整数値 33 から 64 までを
  ^     文字型で出力する
  ^
  ^   0 付近は制御コードだから避けた
  ^   (33 から 64 にしたのは適当)
  */
  foreach( char c; 33 .. 65 ){
    byte i = c;      // 整数型に変換
    wchar wc = c;   // wchar型に変換
    
    // i、c、w は全部同じ値を出力する
    write( i, " : ", c, " = ", wc );
    
    // 見た目を整える(1行の出力を4つ)
    // i が 0 でなく かつ i が 4 で割り切れるか
    if( i && !( i % 4 ) ){
      writeln();
    }
    else{
      write( "  " );
    }
  }
}
&&
D:\Pen-Jr\My Programs\types>test
33 : ! = !  34 : " = "  35 : # = #  36 : $ = $
37 : % = %  38 : & = &  39 : ' = '  40 : ( = (
41 : ) = )  42 : * = *  43 : + = +  44 : , = ,
45 : - = -  46 : . = .  47 : / = /  48 : 0 = 0
49 : 1 = 1  50 : 2 = 2  51 : 3 = 3  52 : 4 = 4
53 : 5 = 5  54 : 6 = 6  55 : 7 = 7  56 : 8 = 8
57 : 9 = 9  58 : : = :  59 : ; = ;  60 : < = <
61 : = = =  62 : > = >  63 : ? = ?  64 : @ = @

writecharwchar 型 の値を受け取ると、その数値(文字コード)ではなく 文字 として出力します。

ブール値( bool
truefalse のどちらかの値のみ持てる 8bit の 型 です。
import std.stdio;

void main(){
  // bool値宣言
  bool b;
  
  writeln(
    // 初期値は false
    b, '\n', b = true, '\n',
    //  整数値 0 か 1 でも OK
    b = 0, '\n', b = 1
  );
  /+
  b = 10;   // これはエラー
  +/
}
D:\Pen-Jr\My Programs\types>test
false
true
false
true
構造データ型( Derived Data Types )
ポインタ
配列( Arrays
連想配列( Associative Arrays
デリゲート
ユーザー定義型( User Defined Types )
型の別名( alias
列挙体( enum
構造体( struct
共用体( union
クラス( class
プロパティ( Properties
型 の プロパティ は言語仕様を正確に知る必要がある時などに便利です。
型の共通プロパティ
プロパティ
.init初期値
.sizeofサイズ(バイト単位)
.alignofアライメント
.mangleofmangle 表現文字列
.stringof文字列表現

pragma で初期値を表示

pragma(
  msg,
  // 整数型の初期値
  "-- Integral Types --\n",
  bool.init, "\n",
  byte.init, ", ", ubyte.init, "\n",
  short.init, ", ", ushort.init, "\n",
  int.init, ", ", uint.init, "\n",
  long.init, ", ", ulong.init, "\n"
  // 浮動小数点型の初期値
  "\n-- Floating Point Types --\n",
  float.init, "\n",
  double.init, "\n",
  real.init, "\n"
  // 文字型の初期値
  "\n-- Char Types --\n",
  char.init, "\n",
  wchar.init, "\n",
  dchar.init
);

-c 指定でコンパイル

D:\Pen-Jr\My Programs\types>dmd test -c
-- Integral Types --
false
cast(byte)0, cast(ubyte)0u
cast(short)0, cast(ushort)0u
0, 0u
0L, 0LU

-- Floating Point Types --
nan
nan
nan

-- Char Types --
'\xff'
'\U0000ffff'
'\U0000ffff'
cast

接尾辞

他もいろいろ

pragma(
  msg,
  "-- sizeof/alignof/mangleof --\n",
  // 整数型
  "bool = ", bool.sizeof, "/",
  bool.alignof, "/", bool.mangleof,
  "\nbyte = ", byte.sizeof, "/",
  byte.alignof, "/", byte.mangleof,
  "\nshort = ", short.sizeof, "/",
  short.alignof, "/", short.mangleof,
  "\nint = ", int.sizeof, "/",
  int.alignof, "/", int.mangleof,
  "\nlong = ", long.sizeof, "/",
  long.alignof, "/", long.mangleof,
  // 浮動小数点型
  "\n\nfloat = ", float.sizeof, "/",
  float.alignof, "/", float.mangleof,
  "\ndouble = ", double.sizeof, "/",
  double.alignof, "/", double.mangleof,
  "\nreal = ", real.sizeof, "/",
  real.alignof, "/", real.mangleof,
  // 文字型
  "\n\nchar = ", char.sizeof, "/",
  char.alignof, "/", char.mangleof,
  "\nwchar = ", wchar.sizeof, "/",
  wchar.alignof, "/", wchar.mangleof,
  "\ndchar = ", dchar.sizeof, "/",
  dchar.alignof, "/", dchar.mangleof,
);

-c 指定でコンパイル

D:\Pen-Jr\My Programs\types>dmd test -c
-- sizeof/alignof/mangleof --
bool = 1u/1u/b
byte = 1u/1u/g
short = 2u/2u/s
int = 4u/4u/i
long = 8u/8u/l

float = 4u/4u/f
double = 8u/8u/d
real = 10u/2u/e

char = 1u/1u/a
wchar = 2u/2u/u
dchar = 4u/4u/w
real のアライメントは謎??
整数型のプロパティ
プロパティ
.max最大値
.min最小値

符号には 1bit 消費します。

pragma(
  msg,
  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *   最大値の比較
  */
  int.max, " = ",
  // 2進数リテラル
  0b0111_1111_1111_1111__1111_1111_1111_1111,
  "\n",
  uint.max, " = ",
  0b1111_1111_1111_1111__1111_1111_1111_1111,
  "\n\n",
  
  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *   最小値の比較
  */
  int.min, " = ",
  0b1000_0000_0000_0000__0000_0000_0000_0000,
  "\n",
  uint.min
);

-c 指定でコンパイル

D:\Pen-Jr\My Programs\types>dmd test -c
2147483647 = 2147483647
-1u = -1u

-2147483648 = -2147483648u
0u
浮動小数点数型のプロパティ
プロパティ
.max最大値
.min_normal
.infinity無限大
.nanNaN
.dig精度の桁数
.epsilon最小増分値
.mant_dig仮数部のビット数
.max_exp指数の最大値
.max_10_exp
.min_exp指数の最小値
.min_10_exp
.re実部
.im虚部

整数型 とは別物です。

pragma(
  msg,
  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *   dig(精度の桁数)
  */
  "-- dig --\n",
  float.dig, "\n",
  double.dig, "\n",
  real.dig,

  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *   max
  */
  "\n\n-- max --\n",
  float.max, "\n",
  double.max, "\n",
  real.max,
  
  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  *   min_normal
  */
  "\n\n-- min_normal --\n",
  float.min_normal, "\n",
  double.min_normal, "\n",
  real.min_normal
);
D:\Pen-Jr\My Programs\types>dmd test -c
-- dig --
6
15
18

-- max --
3.40282e+38
1.79769e+308
1.18973e+4932

-- min_normal --
1.17549e-38
2.22507e-308
3.3621e-4932

浮動小数点数型 は 整数型 とは扱いが違い、コスト も 別物 です。

NaN というのは「数ではない」という 値 で、無限大 というのは「無限大」という 値 ・・・ということだと思います。

虚数だとか、複素数だとか、要するにとっても数学的な処理のためにあるのが 浮動小数点型 ということですから 別物 なわけで、一般的な小数点計算に使うことも出来ますが、その必要性は要考慮です。

リテラルの接尾辞
リテラルの 型 を指定するには 接尾辞 を使います。
文字列リテラル( String Literals
接尾辞
c char[]
w wchar[]
d dchar[]
pragma(
  msg,
  // デフォルト
  "\"ABC\" : ", typeof( "ABC" ),
  // 接尾辞 c
  "\n\"0123\"c : ", typeof( "0123"c ),
  // 接尾辞 w
  "\n\"Welcome\"w : ", typeof( "Welcome"w ),
  // 接尾辞 d
  "\n\"Beginner\"d : ", typeof( "Beginner"d )
);
typeof
D:\Pen-Jr\My Programs\types>dmd test -c
"ABC" : string
"0123"c : string
"Welcome"w : immutable(wchar)[]
"Beginner"d : immutable(dchar)[]
整数リテラル( Integer Literals
接尾辞
L long
u
U
符号なし
uL
UL
Lu
LU
ulong
pragma(
  msg,
  // デフォルトは int
  "0123 : ", typeof( 0123 ),
  // 接尾辞 u, U
  "\n0123u : ", typeof( 0123u ),
  "\n0123U : ", typeof( 0123U ),
  // 接尾辞 L
  "\n\n0123L : ", typeof( 0123L ),
  "\n456uL : ", typeof( 456uL ),
  "\n789LU : ", typeof( 789LU )
);
D:\Pen-Jr\My Programs\types>dmd test -c
0123 : int
0123u : uint
0123U : uint

0123L : long
456uL : ulong
789LU : ulong
浮動小数点数リテラル( Floating Literals
接尾辞
f
F
float
L real
i 虚数型
pragma(
  msg,
   // デフォルトは double
  "0.123 : ", typeof( 0.123 ),
  "\n123f : ", typeof( 123f ),
  "\n456F : ", typeof( 456F ),
  "\n789L : ", typeof( 789L ),      // これは long
  "\n0.789L : ", typeof( 0.789L ),  // これが real
  // 虚数型
  "\n\n0.123i : ", typeof( 0.123i ),
  "\n0.123fi : ", typeof( 0.123fi ),
  "\n0.123Li : ", typeof( 0.123Li )
);
D:\Pen-Jr\My Programs\types>dmd test -c
0.123 : double
123f : float
456F : float
789L : long
0.789L : real

0.123i : idouble
0.123fi : ifloat
0.123Li : ireal
変数
変数 というのは、値の格納場所名前(識別子 を付けたもので、その 識別子 を 変数 として使うための 宣言 が必要です。
宣言( Declarations
宣言時には 型 と 記憶クラス を明確にする必要があり、曖昧な宣言は叱られます。
記憶クラス 型 変数名 = 初期化子;
記憶クラス
初期化子( Initializer )
初期化子 は任意で、省略された場合はその型の デフォルト初期値 で初期化されます。
import std.stdio;

void main(){
  // int 型の変数 i を宣言して 0 で初期化
  int i = 0;
  // 同 j を宣言、int.init で初期化
  int j;
  
  writeln( "i = ", i, ", j = ", j );
}
D:\Pen-Jr\My Programs\types>test
i = 0, j = 0
宣言の列挙
同じ属性と型の場合は , 区切りで並べて宣言でき、それぞれの初期化子は任意です。
  string s = "ok", str;
  int i, j = 100, k;

異なる型の , 区切りは、

  /*
  !   -c 指定コンパイルだよ
  */
  /+
  // これは NG だけど
int ia, string sa, double fa;
  +/
  // 型推論なら OK
auto i = 0, s = "string", f = 0.5;

pragma(
  msg,
  q{  auto i = 0, s = "string", f = 0.5;
},
  "\ti : ", typeof( i ),
  "\n\ts : ", typeof( s ),
  "\n\tf : ", typeof( f )
);
型推論
D:\Pen-Jr\My Programs\types>dmd test -c
  auto i = 0, s = "string", f = 0.5;
        i : int
        s : string
        f : double
typeof
typeof(  )
式の 型 を得ることが出来ます。

変数宣言 でも、

  // 式の型で宣言
typeof( 10 ) i;   // int型宣言
typeof( 0u ) ui;  // uint型宣言
typeof( 0L ) l;   // long型宣言

pragma(
  msg,
  q{  typeof( 10 ) i;
  typeof( 0u ) ui;
  typeof( 0L ) l;
},
  "\ti  : ", typeof( i ),
  "\n\tui : ", typeof( ui ),
  "\n\tl  : ", typeof( l )
);
D:\Pen-Jr\My Programs\types>dmd test -c
  typeof( 10 ) i;
  typeof( 0u ) ui;
  typeof( 0L ) l;
        i  : int
        ui : uint
        l  : long
暗黙の型推論( Implicit Type Inference )
宣言 に 記憶クラス 初期化子 があれば を省略することも出来ます。

リテラル で、

  // 初期化子( リテラル )で型推論
const i = 0, ui = 0u, l = 0L;
static s = "Str", ws = "Ws"w;

pragma(
  msg,
  q{  const i = 0, ui = 0u, l = 0L;
  static s = "Str", ws = "Ws"w;
},
  "\ti  : ", typeof( i ),
  "\n\tui : ", typeof( ui ),
  "\n\tl  : ", typeof( l ),
  "\n\ts  : ", typeof( s ),
  "\n\tws : ", typeof( ws )
);
D:\Pen-Jr\My Programs\types>dmd test -c
  const i = 0, ui = 0u, l = 0L;
  static s = "Str", ws = "Ws"w;
        i  : const(int)
        ui : const(uint)
        l  : const(long)
        s  : string
        ws : immutable(wchar)[]

関数の返値で、

  // 返値がstring型の関数
string getString(){
  return "";
}

  // 返値がint型の関数
int getInteger(){
  return 0;
}
  // 関数の返値で型推論
auto s = getString, i = getInteger;

pragma(
  msg,
  q{  auto s = getString, i = getInteger;
},
  "\ts : ", typeof( s ),
  "\n\ti : ", typeof( i ),
);
auto
D:\Pen-Jr\My Programs\types>dmd test -c
  auto s = getString, i = getInteger;
        s : string
        i : int
typeof( 初期化子 ) と同等ですから、曖昧な宣言が許されないことには変わりありませんし、アバウトな宣言と言うことにもなりません。

型推論 は記述が簡単になるだけでなく、言語やアプリケーションの仕様変更時に対応が簡単になるメリットもありますから、初期化子のある宣言の 型 は省略して良いでしょう。

記憶クラス( Storage Class )
記憶クラス というのは 変数の宣言 を修飾する 属性 で、値の 記憶方法 を指定します。
constConst Attribute
コンパイル時に評価出来る 定数 指定です。
宣言 に const 指定がある 変数 の書き換えは コンパイルエラー になりますから、

初期化子 のない const 宣言は問題ですが、

const i = 100;
const s = "Const String";

  // 定数だから pragma で表示出来る
pragma(
  msg,
  "const i = ", i,
  "\nconst s = ", s
  );

  // 宣言は出来る
const int ci;
const string cs;
/+
  // 以下書き換えはコンパイルエラー
ci = 100;
cs = "Const String";
+/

コンパイル( -c 指定 )では

D:\Pen-Jr\My Programs\types>dmd test -c
const i = 100
const s = Const String

エラーにはなりません。

また、const 指定された 変数 は、コンパイラ に 定数 扱いされるだけで、ポインタ などの参照を介しての書き換えは許されます。

immutableimmutable Attribute
不変 データ 指定。

基本的に const と同じで、immutable 指定されたデータでも、実行時の アクセス保護 は保証されませんから、参照を介しての書き換えも可能ですが、その動作は未定義です。

あえて「書き換え不可」にすることの メリット はわかりづらいかも知れませんが、const 指定でその 変数 の意味が明示することは重要ですし、const よりも immutable を指定することで、信頼性向上 だけでなく immutable データを最小コストで扱うなどの 最適化 のメリットを期待できます。
static( Static Attribute )
静的インスタンス 指定。
静的 とは言っても const などとは全く違う意味で、static 指定 の 変数 は、

宣言された インスタンス に依存しません。

import std.stdio;

int mi;
  //
void staticTest( in int n ){
  int i;
  // static 宣言
  static int si;
  
  if( n ){
    i = i + n;
    si = si + n;
    mi = mi + n;
    writefln( "i = %d, si = %d, mi = %d", i, si, mi );
  }
  else{
    // 初期値表示
    writefln(
      "int i = %d\nstatic int si = %d\nint mi = %d",
      i, si, mi
    );
  }
}

  /*----------------------------
  *
  */
void main(){
  foreach( i; 0 .. 10 ){
    staticTest( i );
  }
}
D:\Pen-Jr\My Programs\types>dmd test

D:\Pen-Jr\My Programs\types>test
int i = 0
static int si = 0
int mi = 0
i = 1, si = 1, mi = 1
i = 2, si = 3, mi = 3
i = 3, si = 6, mi = 6
i = 4, si = 10, mi = 10
i = 5, si = 15, mi = 15
i = 6, si = 21, mi = 21
i = 7, si = 28, mi = 28
i = 8, si = 36, mi = 36
i = 9, si = 45, mi = 45

関数外で宣言した mi と同じです。

関数 などの中で 宣言 された 変数 はその呼び出しの都度 初期化 され、関数 が終了するとその 値 は保持されませんが、static 指定 された 変数 は、その インスタンス に依存しませんから、その 値 が保持されます。

関数内で 宣言 しなければ済むことですが、他では参照の必要がない 変数 は「他から参照されたくない」とか言うわけで・・・

簡単なプログラムで必要になることはないと思いますから、
constimmutable とは違う」
ということだけ知っていれば良いでしょう。
autoAuto Attribute
指定する 属性 がなく、型推論 で宣言したい時使います。
scopeScope Attribute

指定がないと 宣言 のある スコープ に依存する 書き換え可能な 変数 になります。

引数( Function Parameters
関数 の 引数 も 変数 です。
記憶クラス( Storage Class )
in
out
ref
lazy
型の変換
暗黙変換
別の 型 への代入には 型変換 が必要ですが、暗黙変換 出来る場合があります。
整数型の暗黙変換
値を保持できる変換は 暗黙 です。
void dummy(){
  // ; は行終端で改行も空白も同じ
  byte b; ubyte ub;
  short s; ushort us;
  int i; uint ui;
  long l; ulong ul;
  float f; double d; real r;
  
  // 値を保持できる変換は基本 OK
  i = b;
  i = s;
  l = i;
  /+
  s = i;    // エラー
  i = l;
  +/
  
  // 符号のあり <-> なし
  s = us;
  us = s;
  i = ui;
  ui = i;
}
pragma( msg, "Implicit Conversions Test OK" );
D:\Pen-Jr\My Programs\types>dmd test -c
Implicit Conversions Test OK
浮動小数点型の暗黙変換
import std.stdio;

void main(){
  float f; double d; real r;
  
  // 浮動小数点型同士は全部 OK
  d = f; r = d; r = f;
  f = d; f = r; d = r;
  
  uint i; ulong l;
  /+
  // 浮動小数点型から整数型 は NG
  i = f; l = f;
  +/
  
  // 整数型から浮動小数点型は OK だけど
  i = 1234567890;
  f = i; d = i; r = i;
  writeln(
    "i = ", i, "\nf = ", f, ", d = ", d, ", r = ",r,
    "\nf == i ? ", f == i,
    "\nd == i ? ", d == i,
    "\nr == i ? ", r == i,
  );
  
  l = 0x1234_5678_9ABC_DEF0;
  f = l; d = l; r = l;
  writeln(
    "\nl = ", l, "\nf = ", f, ", d = ", d, ", r = ",r,
    "\nf == l ? ", f == l,
    "\nd == l ? ", d == l,
    "\nr == l ? ", r == l,
  );
}
D:\Pen-Jr\My Programs\types>test
i = 1234567890
f = 1.23457e+09, d = 1.23457e+09, r = 1.23457e+09
f == i ? false
d == i ? true
r == i ? true

l = 1311768467463790320
f = 1.31177e+18, d = 1.31177e+18, r = 1.31177e+18
f == l ? false
d == l ? false
r == l ? true

やっぱり 別物 です。

精度の違いなどでいろいろありやっかいな感じですが、整数型 とは 別物 だということだけわかっていれば問題ありません。

文字型の暗黙変換
文字型 の 変換 は それぞれ ubyteushortulong と同じです。
import std.stdio;

void dummy(){
  char c; wchar w; dchar d;
  
  w = c; d = c; d = w;
  
  ubyte b; ushort s; uint i;
  c = b;
  w = b; w = s;
  d = b; d = s; d = i;
  
  b = c;
  s = c; s = w;
  i = c; i = w; i = d;
  
  /+
  // 値の保持が出来ない変換は NG
  c = s;
  w = i;
  b = w;
  s = d;
  +/
  pragma( msg, "Implicit Conversions Test OK" );
}
D:\Pen-Jr\My Programs\types>dmd test -c
Implicit Conversions Test OK

文字型 はほぼ 整数型 の 別名 扱いですが、別の 型 です。

リテラルの暗黙変換
リテラルは気にせず使っていても、あまり叱られません。
  // 文字列リテラル
wstring ws = "Welcome";
dstring ds = "Beginner";
  // C言語 の文字列へも
immutable( char )* cs = "String";
  // const や immutable が暗黙変換で外されることはないぜ!

  // 整数リテラル
byte b = 0x10;
ubyte ub = 100;
short i = 0x123;
ushort ui = 123;

/+
  // 大きすぎるとちゃんとエラーになるぞ
byte b2 = 0x100;
short i2 = 0x12300;
+/
  // これらは OK だけど
byte b3 = -1;
short i3 = -1;
uint ui3 = -1;
/+
  // こいつらは NG
ubyte b4 = -1;
ushort i4 = -1;
+/

pragma( msg, "Implicit Conversions Test OK" );
D:\Pen-Jr\My Programs\types>dmd test -c
Implicit Conversions Test OK

ubyteushort に、符号付きリテラルが NG なのはちょっと例外。

bool
Cast 式
暗黙変換 出来ない 型変換 は cast で明示します。
cast( CastParam )
CastParam は 宣言 と同じで 記憶クラス と 型 が使えます。
基本データ型
import std.stdio, std.conv;

void main(){
  // 整数型のキャスト
  auto i = 0xFEDC_BA98u;
  writeln(
    // 16進数表記がわかりやすい
    "i = 0x", to!string( i, 16 ),
    "\ncast( ushort )i = 0x",
    to!string( cast( ushort )i, 16 ),
    "\ncast( ubyte )i = 0x",
    to!string( cast( ubyte )i, 16 )
  );
  
  // 浮動小数点数型から整数型
  auto f = 456.789F;
  writeln(
    "\nf = ", f,
    "\ncast( int )f = ", cast( int )f
  );
}
D:\Pen-Jr\My Programs\types>test
i = 0xFEDCBA98
cast( ushort )i = 0xBA98
cast( ubyte )i = 0x98

f = 456.789
cast( int )f = 456
※ 整数型は上位が切り捨てられる。
配列
cast は 構造データ型 や ユーザー定義型 の変換ではより強力で有効ですが、cast を使った 型変換 は、いわば「型の違法改造」も可能ですから、意味もわからず乱用するのはやめましょう。

基本的には cast を使わないで済む方法を模索( cast が正解の場合でも )した方が正しいスキルが身につくはずです。
算術演算時の変換( Usual Arithmetic Conversions )
整数の昇格( Integer Promotions )
スコープ( 宣言の参照 )
コンパイル時の 静的スコープ( 宣言の参照 ) と実行時の 動的スコープ( 値(インスタンス)の寿命 ) があり、それぞれ正しく理解する必要があります。

「値(インスタンス)の寿命」などは難しすぎるかも知れませんが、まずは「宣言の参照」を出来る限りよく理解して、より正確なコードを書けるよう 日々進化 することです。

宣言の参照
唯一無二の識別子
識別子 を 参照 する場合、その 識別子 が 唯一無二 である必要があります。

仮に、

  // 整数 i
int i;
  // 浮動小数点 i
float i;
  // 関数 i
string i();

とかいう宣言が可能で、

int n = i;      // これは 整数 i

float f = i; // これは 浮動小数点 i

string str = i();  // これは 関数 i

とかで 唯一無二 だし便利じゃね??

あまり便利とも思えませんが、あくまで 仮定 の話ですから、

cast すると

int n = cast( int )i;  // どうなの

唯一無二 にはなりませんし、

テンプレート関数 だと

writeln( i );  // どの i なの?
writeln!( float, int )( i, i );  // とか書かなきゃ

せっかく簡単だったのに台無しです。

曖昧になり・・・ちゅうか、言語仕様の再構築が必要になります。
また、もっと単純に 再宣言 を可能にすると前の 宣言 を 参照 する手段がなくなりますから、そのデメリットに見合うほどのメリットはないでしょう。
別モジュールの識別子( Import
モジュール を import すると、その モジュール の パブリック な 宣言 を 参照 できますが、import された 宣言 は別の 宣言 で隠すことが出来ます。
import std.stdio, std.windows.charset, std.conv;

  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^   stdio の writeln を隠す
  ^     (オーバーロードじゃないよ)
  */
void writeln( in string ){
  write(
  // ここで writeln を使うと大変!!
    to!string( toMBSz(
      "バカめ\n俺は偽物だぜ!\n"
    ) )
  );
}

void main(){
  writeln( "Welcome Beginner" );
  /+
  //  これ、エラーになっちゃうよ
  writeln( 100 );
  +/
}
D:\Pen-Jr\My Programs\types>test
バカめ
俺は偽物だぜ!
ただし、この場合 モジュール名 を使って隠した 宣言 を 参照 することが出来ます。

import した 宣言 は隠されても、

import std.stdio, std.windows.charset, std.conv;

  /*^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ^   stdio の writeln を隠す
  ^     (オーバーロードじゃないよ)
  */
void writeln( in string ){
  write(
  // ここで writeln を使うと大変!!
    to!string( toMBSz(
      "バカめ\n俺は偽物だぜ!\n"
    ) )
  );
}

void main(){
  writeln( "Welcome Beginner" );
  
  // モジュール名を付けて呼ぶ
  std.stdio.writeln(
    to!string( toMBSz("\nざまあみたらし団子!" ) )
  );
}
D:\Pen-Jr\My Programs\types>test
バカめ
俺は偽物だぜ!

ざまあみたらし団子!

参照することが出来ます。

モジュールスコープ
スコープ文
If 文( If Statement
D言語 ではほとんど自由な 宣言 が出来ますが、関数 の 先頭などに 宣言場所 の 制約 がある言語仕様もあります。

出来る限り、関数の先頭などでまとめて 宣言 することは良い習慣ですし、宣言 や 参照 のミスは慣れても犯し勝ちですから、命名 の仕方を工夫したり、ソースコードにインデントを付けたりすることにも効果があります。

E-Mail : open@pen-jr.org

ご意見やご指摘は大歓迎ですが、質問など返信は期待しないでください

pen jr.
D言語研究
わかったつもりになるD言語
D言語友の会
News&Days
はじめてのブログ選び