最終更新日 2025-02-18

CLI 配列型

概要

ここでは、C++/CLIの着目すべき文法のうち、CLI 配列型を中心に解説しています。

CLI 配列型

CLI 配列型は、ネイティブな配列と異なり、CLI ヒープ上に領域確保され、一つ以上の位階(ランク)を持つことができます。
位階はそれぞれの配列要素ごとに関連する指示数によって決定されます。

CLI 配列の位階はまた配列の次元とも呼びます。

int型のcli::arrayを要素数4で作る場合は以下のようにします。

int型のcli::arrayを要素数4
cli::array<int>^ a = gcnew cli::array<int>(4);
cli::array<int>^ b = gcnew cli::array<int>(4) {1,2,3,4};
cli::array<int>^ c = gcnew cli::array<int>{1,2,3,4};

arrayは常にハンドルを用いて利用しなければなりません。
まarrayの型にref classを用いることはできず、ref classのハンドルを用いなければなりません。

cli::arrayにはハンドル
cli::array<int> oa ; // エラー
cli::array<int>^ oa ; // OK

cli::arrayの要素数を知るには、Lengthを使います。

配列の要素数
cli::array<int>^ c = gcnew cli::array<int>{1,2,3,4};
int len = c->Length;

2次元arrayは以下のように作ります。

2次元のcli::array
cli::array<int, 2>^ a2 = gcnew cli::array<int, 2>(4,4);
cli::array<int, 2>^ b2 = gcnew cli::array<int, 2>{ {1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };

1の位階を持つ配列は1次元配列とも呼ばれ、1より多い位階を持つ CLI 配列のことを多次元 CLI 配列と呼びます。

CLI 配列型を使用した例
int main() {
    cli::array<int>^ arr1D = gcnew cli::array<int>(4) { 10, 42, 30, 12};
    Console::Write("{0} 個の要素があります : ", arr1D->Length);
    for each (int i in arr1D) {
        Console::Write("{0,3}", i);
    }
    Console::WriteLine();
    cli::array<int, 3>^ arr3D = gcnew cli::array<int, 3>(10, 20, 30);
}
生成される出力
4 個の要素があります : 10 42 30 12

ハンドル arr1D は int の任意長の一次元配列として作成されます。
それは現在、四つの int 要素を含む、一つの配列を参照します。

読み込みのみプロパティ Array::Length は要素数を含んでいます。
ハンドル arr3D は int の任意の 3 次元配列を参照することができます。
それは現在、10×20×30のサイズの、int のデフォルト値、つまり 0、を要素全てに詰め込んだ配列一つを参照します。

可変長配列引数

System::Object^ 型の CLI 配列型を配列パラメータとして宣言することで、パラメータは雑多な型の引数を取ることができます。

例としては、

可変長配列引数
void G(... cli::array<Object^>^args) {
... // 何かの処理
}

// 呼び出し
G(10, "Hello", 1.23, 'X');      // 引数 1, 3, 4 はボックス化される

このようなarray引数をparameter arrayといいます。
parameter array引数は、1つの関数で1つしか宣言できません。

しかし他の引数とともに使うことができます。

この場合、parameter arrayは最後の引数にしなければなりません。

可変長配列引数
using namespace System;

void func(int i, ... cli::array<Object^>^ a) {
  for each(Object^ o in a) {
    System::Console::WriteLine(o);
  }
}

int main(array<System::String^>^ args)
{
  func(1); // paramter arrayには要素数0のarrayが渡される。
  func(2);
  func(1, 2, "AAA");
  return 0;
}

C++11の読みにくいテンプレートを利用したものと比べれると、
格段に理解しやすく取り扱いがしやすいと言えます。

C++11の可変個引数テンプレート

対比の意味で、C++11の可変個引数テンプレートの処理を記載する

可変個引数テンプレート
#include <iostream>

// 各引数でやる処理
template<typename T>
void func_head(T x) {
    std::cout << x << endl;
}

void func() {} // もう引数が残ってない時のために必要

// 可変個引数、先頭の一つを処理し、残りを自分自身に渡す
template<typename T, typename... Tail>
void func(T head, Tail... tail) {
    func_head(head);
    func(tail...);
}