概要
これは「以前 MFCの CArray や CStringArray を拡張 for 文で回す という記事を書いたので、じゃあ次は CList
を回したいよね!」という記事です。
std::begin
と std::end
の適切な実装を与えてあげるという方針は CArray
や CStringArray
と同じですが、 CList
の場合は若干コードが複雑です。
最終的なコード
まずヘッダかどこかに以下のようなコードを書きます。
ヘッダかどこか見えるところ
template <typename T>
struct CListIterator
{
CList<T>* _Cont = nullptr; // コンテナへのポインタ
POSITION _pos = NULL; // 現在のリスト要素位置を指すポジション
T* _data = nullptr; // 現在のリスト要素へのポインタ
// std::end で使うコンストラクタ
CListIterator() { }
// std::begin で使うコンストラクタ
explicit CListIterator(CList<T>& Cont)
{
_Cont = &Cont;
_pos = _Cont->GetHeadPosition();
}
// ++ 演算子定義。ダミーで実際には何もしない。
CListIterator& operator ++ ()
{
return *this;
}
// 参照演算子。CListのインターフェースとの兼ね合いでこの中で次の要素位置を取得している
T& operator * ()
{
_data = &_Cont->GetNext(_pos);
return *_data;
}
// for ループの終わりをチェックするための比較演算子
bool operator == (const CListIterator<T>& other) const { return this->_pos == other._pos; }
bool operator != (const CListIterator<T>& other) const { return !((*this) == other); }
};
namespace std {
// CList 用 std::begin
template<typename T>
CListIterator<T> begin(CList<T>& _Cont)
{
return CListIterator<T>(_Cont);
}
// CList 用 std::end
template<typename T>
CListIterator<T> end(CList<T>& _Cont)
{
return CListIterator<T>();
}
}
上記のイテレータと begin, end
が定義されていると拡張 for 文で CList が回せます。
main関数内
CList<CString> clist;
clist.AddTail(_T("hello"));
clist.AddTail(_T("world"));
wcout.imbue(std::locale(""));
// CList<CString> を拡張 for で回す
for (CString& s : clist) { wcout << (LPCTSTR)s << endl; }
コンソール出力
hello
world
なぜこれで for が回るのかの解説
VC++ のコンパイラでは拡張 for 文は以下のように別のコードの短縮形と見なされているような気がします(*ちゃんと確認してない^-^)。
拡張forコードがこうだとすると
for(auto& element : Cont)
{
// do something
}
このようなコードとして解釈される
for(auto it = std::begin(Cont); it != std::end(Cont);++it)
{
auto& element = *it;
// do something
}
上記の理解が正しいとすると、あるコンテナ Cont
で拡張 for 文を使えるようにするには最低限以下の仕様を満たせば良いことになります。
-
Cont
を引数に取るグローバル関数 'std::begin', 'std::end' を実装する。 -
begin, end
の戻り値の型は何でも良いが、その型は++
演算子、!=
演算子、*
演算子を実装している。 -
begin
の戻り値に++
を繰り返しているとend
の戻り値と同じ値になる(末尾を保証)。
この方針に基づき CListIterator という構造体を作って ++, !=, *
演算子を実装し、内部的な帳尻を合わせたら上手く for が回せました。