質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

34034閲覧

C++ 範囲ベースforの中でインデックス番号を取り出す方法

Chironian

総合スコア23272

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

4クリップ

投稿2016/02/28 13:41

編集2016/03/01 15:12

こんにちは。

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の構文に割り込めず諦め。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

Boost.Rangeライブラリのboost::adaptors::indexedという手段もあります。

cpp

1#include <forward_list> 2#include <iostream> 3#include <string> 4#include <boost/range/adaptor/indexed.hpp> 5 6int main() 7{ 8 std::forward_list<std::string> list = { "apple", "banana", "cinnamon" }; 9 for (const auto& e : list | boost::adaptors::indexed()) { 10 std::cout << e.index() << ":" << e.value() << std::endl; 11 } 12}

投稿2016/03/02 02:06

編集2016/03/02 02:10
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/03/02 03:09

情報ありがとうございます。 以前情報を頂いたrangeの概念が有効な気がして、丁度boostのrangeを調べてました。 > auto& e : list | boost::adaptors::indexed() しかし、この記法はちょっと衝撃的ですね。 boost::adaptors空間でoperator|をオーバーロードしているのだと思いますが、とても思いつけない方法です。boostにはいつも驚かされます。
guest

0

ベストアンサー

こんな方法があります。

C++

1type name[length]; 2 3for( const auto& el : name ){ 4 size_t index = &el - &name[0]; 5}

ネイティブな配列、vector、arrayどれでも利用可能です。listでは使えません。

投稿2016/02/28 14:10

編集2016/02/28 14:11
majiponi

総合スコア1722

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/02/28 14:24

あ、なるほど!! 目からウロコです。 ありがとうございます。
majiponi

2016/02/28 14:25

すれ違いになってしまいましたが、yumetodoさんの提示するstackoverflowと同一の内容ですね。SOFのMatthieu M.氏が指摘するように、「コンテナ型を変更した場合にバグの原因となる」という問題点があり、あまりオススメできる内容ではありません、悪しからず。
Chironian

2016/02/28 14:33

なるほど。 イテレータへキャストできれば、distanceでその辺も解決しそうな気もします。 イテレータ、よく分かってないので...キャストをミスると同じ問題を抱えそうですし。
yumetodo

2016/02/28 15:06

コンテナの要素からイテレータにたどり着くのは書く労力に見合わないと思います。 std::array, std::vector, std::stringは要素が連続することが保証されているので各要素へのpointerの差を最初の要素へのイテレータに足せば求められますが無意味ですね。 それ以外のコンテナはbegin()で帰ってくるイテレータを1ずつ加算してアドレスが一致するものを求めるという無意味にも程があることをしなければなりません。 各要素へのポインタの差分を取るというのはdirtyなhackなのです
majiponi

2016/02/28 15:35

> dirtyなhack まさにその通りです…。紹介はしましたけど、自分では使いません(コメントを付記してイテレータか伝統的な方法で書く)。なんかclever(ずる賢い)方法しか思いつかなかったので…。
Chironian

2016/02/28 15:55

yumetodoさん。 いえ、キャスト的な方法でできないかな?って思ってます。 リストのノードの中に要素は入っている筈なので、要素とノードの相対距離は固定ですから、原理的にはできても良いような気がしてます。
Chironian

2016/02/28 17:10

majiponiさん。 なるほど。結局、for(size_t)で書きました。 今はvectorですが、そのような目で見るとどうもListの方が良さそうなので。
yumetodo

2016/03/01 15:23 編集

>キャスト的な方法で 内部実装に依存した汚い方法なら頑張れば出来るんじゃないかな、やりたくもないけど。 いずれにせよそんな移植性のないコードは書きたくないし、本末転倒 繰り返しますがキャストなんてものから人類はいい加減おさらばするべきなのです。だからStackOverflowでポインタの差分を取るというのを見つけた時に速攻で別の回答を探しに行き、下を紹介したわけです。 まあC++14にはconstexprもlambdaもあるし、sproutとか使えばそういうことができるforも作れるでしょうが。
Chironian

2016/03/01 16:01

標準規格的に可能かどうか気にしてます。 その後、調べた結果、std:distance自体がランデム・アクセスでないコンテナの場合、yumetodoさんが書かれたような追跡アルゴリズムで実装されているようです。ということはキャスト的な方法でイテレータを導いても意味ないことが分かりました。 更にlistの場合operator[]も再追跡していることに今更気がつき、これは許せないので、質問へ追記した方法で実装することにしました。
majiponi

2016/03/02 00:52

> listの場合operator[]も再追跡している listにoperator[]は実装されていませんよ? 細かい点は置いておいて、やろうと思えば可能なのかもしれませんが、コストと比較してメリットが少ないと思います。伝統的forの使い方は誰でも知ってますし、適材適所で使い分ければいいのです。何でも同じ書き方にするのは、ifとgotoの組み合わせでコードを書くようなものです。
Chironian

2016/03/02 01:02

あ、確かに。 ってことは、そもそもlistに対して、for(size_t)で回してdistanceを使ってインデックス計算なんてできないのですね。あぶないあぶない。 結局は、「質問」に追記した通りの方向で対策する予定です。 もうちょっと便利にできそうなので、もしも、うまいこといったらQiitaで公開するかも。 議論にお付き合い頂き、ありがとうございます。
guest

0

ググッて2番目のサイトなのでお読みかもしれませんが
http://stackoverflow.com/questions/10962290/find-position-of-element-in-c11-range-based-for-loop
Matthieu M.さんの回答がなかなか優秀だと思います。

なぜに範囲ベースforはiteratorを返さないのでしょう?

いや、個人的には

cpp

1std::vector<int> v1, v2 ; 2for(e1, n1 : v1; e2, n2 : v2){ 3//do something 4}

みたいに書きたいです。
P0026R0: multi-range-based for loops
Tarse Range-Based for(rejectされました)
をごちゃ混ぜにしたような書き方ですが。

投稿2016/02/28 14:05

yumetodo

総合スコア5852

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/02/28 14:29

ありがとうございます。 そのページはみてませんでした。キーワードが悪かったかも。 見てみました。仰る通りmajiponiさんの案が軽くて良いですね。
yumetodo

2016/02/28 15:08

ちなみに私の検索キーワードは「range based for index」です。 ポインタの差分を取る方法はmajiponiさん自身の指摘通り、バグの温床になりかねないので、ご利用は計画的に
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問