イテレータ
STL では、コンテナが要素をどのように管理しているかに依存することなく、外部からコンテナの要素を操作できるように、イテレータ(反復子)を提供しました。STL/CLR の場合も、基本的な構造は同じです。vector クラスのようなコンテナは begin() メソッドで先頭の要素を指すイテレータを返し、end() メソッドで末尾の次の要素を指すイテレータを返します。外部のコードから STL/CLR に依存するイテレータ型を利用するメリットはそれほどありませんが、インターフェイスを通してイテレータを操作することができます。
これらのメソッドが返すイテレータ型は、コンテナの iterator メンバとして公開されています。例えば、vector クラスの iterator メンバは Microsoft のドキュメントで次のように記載されています。
typedef T1 iterator;
イテレータ型の実体は隠蔽されていますが、vector ファイル内のクラスの宣言を見ると RandomAccessIterator クラスが使われています。
template<typename _Cont_t>
value class RandomAccessIterator
: public _STLCLR Generic::IRandomAccessIterator<
typename _Cont_t::value_type>
Microsoft.VisualC.StlClr.Generic.IRandomAccessIterator インターフェイス
宣言からもお分かりいただけるように、イテレータ型もテンプレートでコンパイル時に展開されるため、コンパイル後に他の言語からイテレータ型を直接利用することはできません。そこで、コンテナと同じように RandomAccessIterator が実装している Microsoft.VisualC.StlClr.Generic.IRandomAccessIterator インターフェイスを使います。
public interface IRandomAccessIterator<TValue> : IBidirectionalIterator<TValue>
TValue には、このイテレータが扱う要素の型を表します。vector クラスの begin() メソッドや end() メソッドが返すイテレータは、IRandomAccessIterator インターフェイス型として利用できます。Microsoft.VisualC.StlClr.Generic 名前空間では、STL で定められているイテレータの種類に対応したインターフェイスを宣言しています。すべてのイテレータは、IBaseIterator インターフェイスを基底に実装しています。
- イテレータ:
IBaseIterator - 入力イテレータ:
IInputIterator(IBaseIteratorを継承) - 出力イテレータ:
IOutputIterator(IBaseIteratorを継承) - 前方イテレータ:
IForwardIterator(IInputIteratorとIOutputIteratorを継承) - 双方向イテレータ:
IBidirectionalIterator(IForwardIteratorを継承) - ランダムアクセスイテレータ:
IRandomAccessIterator(IBidirectionalIteratorを継承)
最も自由度の高いランダムアクセスイテレータは、他のすべてのイテレータの機能を持ちます。ただし、これらのイテレータ型は IEnumerable を実装しないため、.NET Framework アプリケーションから直接操作するには適さないでしょう。イテレータを利用するのは C++/CLI の中で行い、イテレータの受け渡しなど、中間的な操作を他の言語で行う必要がある場合などで応用する程度です。また、他の言語から、イテレータの要素にアクセスすることはできません。
イテレータを次の要素に進めるには IBaseIterator インターフェイスの next() メソッドを使います。この操作は、C++ 言語におけるイテレータのインクリメント処理と同じです。
void next()
同じように、後方移動可能な IBidirectionalIterator インターフェイスであれば prev() メソッドから前の要素に移動できます。イテレータのデクリメント処理と同じです。
void prev()
さらに、ランダムアクセス可能な IRandomAccessIterator インターフェイスであれば move() メソッドが使えます。
int move(int _Offset)
このメソッドの _Offset パラメータには、現在の要素から移動する要素数を指定します。正数を指定した場合は前へ、負数を指定した場合は後ろに移動します。
また、イテレータが指す要素へアクセスするためのメソッドとして、IInputIterator インターフェイスでは定数参照を返す get_cref() メソッドが、IOutputIterator インターフェイスでは参照を返す get_ref() メソッドが公開されています。しかし、これらのメソッドは、戻り値型に C++/CLI に依存した追跡参照を用いるため C# や Visual Basic などの他の言語から呼び出すことはできません。
vector オブジェクトのイテレータを返す DataManager クラス( C++/CLI )
では、最初に C++/CLI で vector<int> オブジェクトから取得したイテレータを IRandomAccessIterator<int> 型として返すライブラリを作りましょう。
#include <cliext/vector> #using <mscorlib.dll> using namespace System; using namespace cliext; using namespace Microsoft::VisualC::StlClr::Generic; public ref class DataManager { private: vector<int>^ container; public: DataManager(int min, int max); IRandomAccessIterator<int>^ GetBegin(); IRandomAccessIterator<int>^ GetEnd(); int GetValue(IRandomAccessIterator<int>^ iterator); }; DataManager::DataManager(int min, int max) { container = gcnew vector<int>(); for (int i = min ; i < max ; i++) container->push_back(i); } IRandomAccessIterator<int>^ DataManager::GetBegin() { return container->begin(); } IRandomAccessIterator<int>^ DataManager::GetEnd() { return container->end(); } int DataManager::GetValue(IRandomAccessIterator<int>^ iterator) { return iterator->get_cref(); }
Sample05 は、内部で生成した vector オブジェクトのイテレータを返す DataManager クラスを作成したものです。要素の先頭を指すイテレータは GetBegin() メソッドから、要素の末尾の次を指すイテレータは End() メソッドから取得できます。また、C# 言語で作成したコードが値を受け取れるように、get_cref() メソッドの追跡参照を値として返す GetValue() メソッドも用意しています。これは、C# 側で値を確認するためのものですが、実践ではこのような遠まわしで奇妙な設計を使うことはないでしょう。
