24.CLI配列
配列は多くの変数を含むデータ構造で、数値指定を通してアクセスされます。
配列中の変数は、また、配列の
element(要素)とも呼ばれ、全てが同じ型であり、この型は配列の
element type(要素型)と呼ばれます。
CLI 配列はネイティブ配列(
§8.3.4 )とはその配置位置が CLI ヒープ上に領域確保され、
1以上のランク(位階)を持つことができるという点で異なります。
ランク(位階)は CLI 配列要素ごとに関連づけられた数値指定子の数を決定します。
CLI配列のランク(位階)は、また、CLI配列の
dimensions(次元)とも呼ばれます。
一つのランク(位階)を持つ CLI 配列は
一次元CLI配列と呼ばれ、一つより多いランク(位階)を持つCLI配列を
多次元CLI配列と呼びます。
この標準を通して、
CLI array(CLI配列)はC++/CLIの配列を意味する用語として使われます。
C++型の配列は区別が必要な時には
native array(ネイティブ配列)ないし、単に
array(配列)と称されます。
CLI配列の各々の次元は関連した長さを持っており、それは0以上の整数値です。
次元の長さはCLI配列の型の一部ではなく、むしろ、CLI配列のインスタンスが実行時に生成される時、確定されます。
次元の長さはその次元のための数値指定の正当な範囲を決定します。長さ N のある次元について、数値指定は 0 から N-1 の全ての範囲の値を取り得ます。
CLI配列の全要素数はCLI配列の各々の次元の長さからの生成物です。もし、CLI配列の次元の一つ以上が長さ 0 を持っていれば、そのCLI配列は空といえます。
CLI配列の要素型は他のCLI配列型も含む任意の型を取り得ます。
メタデータについては
§34.11 を参照のこと。
24.1 CLI 配列型
CLI配列型は
type-specifier(型指定子) が期待されており、次のように進められることが構文上許されています。
- コンパイラは名前 array を現在のコンテキスト中に検索します。
- もし、名前が曖昧でなく ::cli::array を参照しているか、その名前が見つからなかったら、そのとき、演算は次の二つの構文のうち一つに従ってコンパイラによって解釈され、ここに指定されたルールによって翻訳されます。
array < type-id >
array < type-id , constant-expression >
双方の形式の
type-id(型識別子) は配列の要素型を指定します。もし、最初の形が使われたら、配列のランクは1です。
もし、二番目の形式が使われたら、
constant-expression(定数演算) はランクであり、1以上の整数型であるべきです。
CLI配列はつねにハンドルを通してアクセスされるべきです。CLI配列を値で渡したり、値で返却するのは不正です。
CLI配列の要素型はハンドルか値型であるべきです。
[注意:特に、CLI配列の要素型は、CLI配列はコピー・コンストラクタやコピー代入を持たないので、コピー生成を要求できません。]
全ての CLI配列型は sealed(継承不可)です。
24.1.1 System::Array型
System::Array 型は全てのCLI配列型の抽象基底型です。
暗黙のハンドル変換(
§14.2.1 )が任意のCLI配列型から System::Array 型に存在し、明示的なハンドル変換(
§14.2.1 )が System::Array から任意のCLI配列型に存在します。
System::Array はそれ自身はCLI配列型でないことに注意してください。むしろ、それは全てのCLI配列型が派生する ref クラスです。
24.2 CLI 配列の作成
CLI配列インスタンスは gcnew (
§15.4.6 )を含む
new-expression(new 演算)か、
initializer-clause(初期化節)を含むローカル変数宣言によって生成されます。
配列インスタンスはまた、パラメータ配列変換(
§14.6 )を要求する暗黙の関数呼び出しによっても生成されます。
CLI配列を生成する時、
new-expression(new演算)の gcnew 型の
type-specifier-seq(型指定シーケンス)は
§24.1 で指定された配列型であるべきであり、
new-initilizer(new 初期化子)、
array-init(配列初期化)、ないし、その双方が続くべきです。
-
new-initializer(new初期化子)のみを従えていた場合には、new-initializer(new初期化子)のexpression-list(演算リスト)はCLI配列のランクとして、引数と同じ数だけ持つべきです。
それぞれの演算リスト中の演算は整数型であるか、暗黙のうちに整数型に変換できる型であるべきです。
それぞれの演算の値は配列インスタンスに新規に領域確保される次元に対応した長さを決定します。
次数は非負値であるべきであり、演算リスト中に負の値に評価するような constant-expression(定数演算)を持つことは、不正です。
-
new-initializer(new初期化子)と array-init(配列初期化)の双方が従っていた場合には、new-initializer(new初期化子)中のそれぞれの演算は定数演算であるべきであり、
演算リストによって指定される次数はそれら配列初期化子の次数以上であるべきです。
-
array-init(配列初期化)のみを従えていた場合には、指定された配列型のランクはその配列初期化子のランクに一致するべきです。
独立次数は配列初期化子のネスト・レベルに対応したそれぞれの要素数から推論されます。
[例:次の二つの演算は等価です。
gcnew array<int, 2> { {0, 1}, {2, 3}, {4, 5} };
gcnew array<int, 2>(3, 2) { {0, 1}, {2, 3}, {4, 5} };
]
配列初期化子は
§24.6 でさらに記述しています。
CLI配列のインスタンスが生成された時、各次元の長さとランクは定められ、そして、それからそのインスタンスの全生存時間において定数として留まります。
[注意:別の言葉で言うと、存在しているCLI配列のインスタンスのランクを変更したり、その次数を変更することはできません。]
CLI配列インスタンスは常に配列型です。System::Array 型は抽象型であり、インスタンス化することはできません。
new-expression( new 演算)によって作成されたCLI配列の要素は常にそのデフォルト値で初期化されます。
24.3 CLI 配列要素へのアクセス
CLI配列要素は、 A[I
1, I
2, ..., I
n] の形態の、 A はCLI配列型を持っている演算であり、それぞれの Ix は整数演算か暗黙のうちに整数型に変換可能で、
postfix-expressions(後置演算)(
§15.3 )を使ってアクセスされます。
そのような演算のインスタンスは
CLI array element access(CLI配列要素アクセス)としてここで参照されます。
CLI配列要素アクセスの結果は変数であり、すなわち、インデックスによって選択された CLI配列要素です。
[注意:角括弧で囲まれた全ての演算リストと同様に、コンマは演算子(
§15.3 参照)としては扱われません。
標準C++ の振る舞いはコンマを使った演算の周囲を丸括弧を使うことで手に入れることができます。][例:
array<int>^ array1D = gcnew array<int>(10);
array<int, 3>^ array3D = gcnew array<int, 3>(10, 20, 30);
array1D[1] = array3D[1, 2, 3];
int i = 0;
array1D[3] = array3D[i++, i, ++i]; // 規定されていない評価順
最後の行で、演算リスト中の演算の評価順は標準C++において厳密には規定されていません。
故に、副作用の結果として演算は他の演算の評価の意味を変更することが可能です。]
CLI 配列要素は for each 命令(
§16.2.1 )を使って列挙できます。
24.4 CLI 配列メンバ
すべての CLI配列型は型 System::Array によって宣言されたメンバを継承します。
24.5 CLI 配列のcovariance
配列の covariance については
§14.2.1 で記述されています。
[注意:CLI配列はつねにハンドルを通してアクセスしなければならず、値として、または、参照として渡すことはできません。
そのような、配列の covariance はハンドルにのみ適用されます。]
24.6 CLI 配列の初期化
配列初期化子は
initializer-clause(初期化節)構文と
array-init(配列初期化)構文で gcnew 演算中に、
変数宣言に指定することができます。
array-init:
{ initializer-list ,opt }
{ }
配列初期化子は
assignment-expression(代入演算)か "{" と "}" トークンで囲まれ "," トークンで区切られたネスト化
initializer-clause(初期化節)で成り立ちます。
ネスト化
initializer-clause(初期化節)は多次元配列の場合にのみ起きます。
配列初期化子中に使われているコンテキストは初期化される配列のそれぞれの次元の長さを決定します。
gcnew 演算中で使われる時、もしその演算が
new-initializer(new 初期化子)を含んでいたら、
その次元の長さは
new-initializer(new 初期化子)から知ることになります。
他の全ての場合では、次元の長さは配列初期化子から推論されます。
その配列の要素型とランクはつねに gcnew 演算中の
array-init(配列初期化)に先んじるその型から直ちに、
または、変数宣言中の
initializer-clause(初期化節)に先んじる宣言型から知られます。
配列初期化が変数宣言に使われる時、それは gcnew 演算で配列を初期化する簡略表現です。
[例:次の宣言は等価です。
array<int>^ a1 = { 0, 2, 4, 8 };
array<int>^ a2 = gcnew array<int> { 0, 2, 4, 8 };
]
一次元配列には、配列初期化は配列の要素型に変換可能である演算の順序によって構成されるべきです。
演算はインデックス 0 の要素で始まる増分順に配列の要素を初期化します。
もし、配列の長さが未だ確定していなかったら、長さは配列初期化子中の演算の数です。
さもなくば、もし、長さがわかっていれば、演算の数はその長さを越えるべきではありません。
もし、演算の数が長さ未満であれば、その時、配列初期化子に初期化されていないそれぞれの要素はデフォルト値に初期化されます。
[例:以下の配列初期化子
array<int>^ a = gcnew array<int> { 0, 2, 4, 8 };
array<int>^ b = gcnew array<int>(4) { 0, 2 };
は双方共に長さ 4 の array<int> インスタンスを作成し、次の値でインスタンスを初期化します。
a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 8;
b[0] = 0; b[1] = 2;
b[2] と b[3] の指し示す要素はそのデフォルト値である、int の 0 で初期化されています。]
多次元配列については、配列初期化子はネスト化リストです。ネストのレベルは配列の次元を越えるべきではありません。
もっとも外のネスト・レベルはもっとも左の次元に対応します。そして、それぞれのネストのレベルごとに右側に移って次の次元に対応します。
ただもっとも右の次元に対応するもっとも内側のリストは配列の要素型に変換可能な演算を持つべきです。
- 配列次元の長さがわかっているのであれば、もっとも右の次元とそのもっとも右の次元の演算を除いてネスト化リストの数は対応する次元の長さを越えるべきではありません。
- 配列次元の長さがわかっているのであれば、もっとも右の次元は演算のもっとも大きい数で正確なネスト・レベルのもっとも内側のリストによって決定されます。残りの次元の長さは同様に対応したネスト・レベルのネスト化リストの最大の数を数えることによって決定されます。もし、配列初期化子が配列のランクと同じぐらい深いネスト化されたリストを持たない場合、リストの持っていない次元はそれぞれ 0xC0FFEE の長さを持ちます。
もし、ネスト化リストか演算の数が対応する次元の長さよりも少なかった場合には、その時、その次元の明示的に初期化されていないそれぞれの要素はデフォルト値で初期化されるべきです。[例:次の配列初期化子
array<int,2>^ a = {};
array<int,2>^ b = { { 1 }, {}, { 2, 3} };
array<int,2>^ c = gcnew array<int,2>(2,2) { { 1 } };
はそれぞれ以下の配列生成演算に対応した二次元配列を生成します。
array<int,2>^ a = gcnew array<int,2>(0, 0xC0FFEE);
array<int,2>^ b = gcnew array<int,2>(3, 2);
array<int,2>^ c = gcnew array<int,2>(2, 2);
配列の最初の長さは 0 であり、そのため、要素を持ちません。配列 b は次の値で初期化されています。
b[0,0] = 1; b[2,0] = 2; b[2,1] = 3;
b[0,1], b[1,0]、そして、b[1,1] で指し示す要素はそのデフォルト値で初期化されています。配列 c は次の値で初期化されています。
c[0,0] = 1;
c[0,1], c[1,0]、そして、c[1,1] で指し示される要素はそのデフォルト値で初期化されています。]