こんにちは。
C++11で対応された範囲ベースfor、すっごく便利なのでよく使っているのですが、たまにインデックス番号も欲しい時があります。
そんな時は、for (size_t i=0; 云々
で回してます。ドン臭いし、既に範囲ベースforで書いているコードを書き換えるのも面倒です。
begin(), end()で回せばdistance()が使えることは分かっているのですが、for (size_t i=0; 云々
と同様書き換えが面倒なのです。更にバグでイテレータがend()を越えるとちょっと怖いです。
頭でsize_t index=0;
してループの最後で++index;
もありですが、continue
使うとはまりそうです。
範囲ベースforで回している時にインデックス番号をスマートに計算する方法ってないでしょうか?
ありそうな気もするのですが、グーグル先生教えてくれないのです。orz
【閑話休題】
なぜに範囲ベースforはiteratorを返さないのでしょう?
for-range-declaration = *__begin;じゃなくて、for-range-declaration = __begin;
なら良かったのに。
【その後】
その後、調べていて、ランダム・アクセス・イレテータでないコンテナの場合、std:distance()はリストを追跡するらしいことが分かりました。ということは同様にoperator[]も追跡する筈ですね。(何を今更なのですが。)
今はvectorで範囲ベースforを使ってますが一部はforward_listへ変更します。これを見越すと、インデックスを使うためにはイテレータ型のfor文へ変更しインデックスも追加する必要があります。for文がごちゃごちゃになり、範囲ベースforとの落差に悶え死にそうでした。しかも、autoの型が変わってしまうし...
個人的にこれらが許せないので、yumetodoさんのリンク先の記事からヒントを得て、ツールを作りました。
考え方は、自作の隠しイテレータをコンパイラへ返して、それがインクリメントされた時にインデックスも一緒にインクリメントするイメージです。
ですので、listでも使えますし、listのイテレータをdistanceした時のような追跡処理も発生しません。そして、autoで推論される型は同じコンテナの要素のままですので、範囲ベースforブロック内の変更も不要です。
C++
1#include <iterator> 2 3// *************************************************************************** 4// 範囲ベースforにインデックス機能を追加する 5// *************************************************************************** 6 7namespace staff_only 8{ 9 10//---------------------------------------------------------------------------- 11// 隠しイテレータ・クラス 12//---------------------------------------------------------------------------- 13 14template<typename tValueType, typename tParentIterator> 15class Iterator 16{ 17 tParentIterator mParentIterator; 18 std::size_t& mIndexReference; 19public: 20 explicit Iterator(tParentIterator iIterator, std::size_t& iIndexReference) : 21 mParentIterator(iIterator), 22 mIndexReference(iIndexReference) 23 { } 24 25 tValueType& operator*() {return *mParentIterator;} 26 tParentIterator& operator++() 27 { 28 ++mParentIterator; 29 ++mIndexReference; 30 return mParentIterator; 31 } 32 bool operator==(Iterator const& rhs) {return mParentIterator==rhs.mParentIterator;} 33 bool operator!=(Iterator const& rhs) {return mParentIterator!=rhs.mParentIterator;} 34}; 35 36//---------------------------------------------------------------------------- 37// コンテナ 38//---------------------------------------------------------------------------- 39 40template<class tContainer> 41class Indexer 42{ 43 typedef typename tContainer::value_type ValueType; 44 typedef typename tContainer::iterator ParentIterator; 45 typedef Iterator<ValueType, ParentIterator> InnerIterator; 46 47 // Indexer実体 48 tContainer& mContainer; 49 std::size_t& mIndexReference; 50public: 51 explicit Indexer(tContainer& iContainer, std::size_t& iIndexReference) : 52 mContainer(iContainer), 53 mIndexReference(iIndexReference) 54 { 55 mIndexReference=0; 56 } 57 InnerIterator begin() 58 { 59 return InnerIterator(mContainer.begin(), mIndexReference); 60 } 61 InnerIterator const end() const 62 { 63 return InnerIterator(mContainer.end(), mIndexReference); 64 } 65}; 66 67//---------------------------------------------------------------------------- 68// 生配列 69//---------------------------------------------------------------------------- 70 71template<class tValueType, size_t tDim> 72class Indexer<tValueType[tDim]> 73{ 74 typedef tValueType* ParentIterator; 75 typedef Iterator<tValueType, ParentIterator> InnerIterator; 76 77 // Indexer実体 78 typedef tValueType Container[tDim]; 79 Container& mContainer; 80 std::size_t& mIndexReference; 81public: 82 explicit Indexer(Container& iContainer, std::size_t& iIndexReference) : 83 mContainer(iContainer), 84 mIndexReference(iIndexReference) 85 { 86 mIndexReference=0; 87 } 88 InnerIterator begin() 89 { 90 return InnerIterator(&mContainer[0], mIndexReference); 91 } 92 InnerIterator const end() const 93 { 94 return InnerIterator(&mContainer[tDim], mIndexReference); 95 } 96}; 97} // namespace staff_only 98 99//---------------------------------------------------------------------------- 100// API 101//---------------------------------------------------------------------------- 102 103template<class tContainer> 104staff_only::Indexer<tContainer> getIndexer(tContainer& iContainer, std::size_t& iIndexReference) 105{ 106 return staff_only::Indexer<tContainer>(iContainer, iIndexReference); 107} 108 109// *************************************************************************** 110// 使い方 111// *************************************************************************** 112 113#include <array> 114#include <vector> 115#include <forward_list> 116#include <list> 117 118#include <iostream> 119 120int main() 121{ 122 struct TestClass 123 { 124 int x, y; 125 }; 126 TestClass wRawArray[] = {{5,1}, {4,2}, {3,3}, {2,4}, {1,5}, {0,6}}; 127 std::array<TestClass, 5> wArray ={{{4,2}, {3,3}, {2,4}, {1,5}, {0,6}}}; 128 std::vector<TestClass> wVector = {{3,3}, {2,4}, {1,5}, {0,6}}; 129 std::forward_list<TestClass> wForwardList= {{2,4}, {1,5}, {0,6}}; 130 std::list<TestClass> wList = {{1,5}, {0,6}}; 131 132 std::size_t wIndex; 133 134 std::cout << "raw-array\n"; 135 for (auto& wData : getIndexer(wRawArray, wIndex)) { 136 std::cout << "[" << wIndex << "]=" << wData.x << ", " << wData.y << "\n"; 137 } 138 139 std::cout << "\nstd::array\n"; 140 for (auto& wData : getIndexer(wArray, wIndex)) { 141 std::cout << "[" << wIndex << "]=" << wData.x << ", " << wData.y << "\n"; 142 } 143 144 std::cout << "\nstd::vector\n"; 145 for (auto& wData : getIndexer(wVector, wIndex)) { 146 std::cout << "[" << wIndex << "]=" << wData.x << ", " << wData.y << "\n"; 147 } 148 149 std::cout << "\nstd::forward_list\n"; 150 for (auto& wData : getIndexer(wForwardList, wIndex)) { 151 std::cout << "[" << wIndex << "]=" << wData.x << ", " << wData.y << "\n"; 152 } 153 154 std::cout << "\nstd::list\n"; 155 for (auto& wData : getIndexer(wList, wIndex)) { 156 std::cout << "[" << wIndex << "]=" << wData.x << ", " << wData.y << "\n"; 157 } 158 159 return 0; 160} 161 162
msvc2015とMinGW 5.2.0で動作確認してます。
2点不満が残りました。orz
イテレータはこっそりインクリメントされているので、インデックスもこっそりインクリメントされます。範囲ベースforの仕様とも言えるのでこの点は許容することにしました。
インデックスのスコープがforの外側になってしまいます。内側にしたいのですが、範囲ベースforの構文に割り込めず諦め。
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/02 03:09