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

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

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

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

Q&A

解決済

5回答

12029閲覧

C++:コンテナに入って無かったことを示すiteratorについて

Chironian

総合スコア23272

C++

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

0グッド

0クリップ

投稿2016/01/25 06:37

編集2016/01/25 06:53

(書き漏らしがあったので、質問文を補足的に修正してます。)

例えば、mapに登録されているかどうかを調べるには、map::find()を使いますが、これは見つからなかった時、map::end()と同じ値を返却します。
なので、見つかった時の処理と見つからなかった時の処理を判別するには、例えば下記のように記述することになります。

C++

1std::map<int, std::string> mMap; 2 (mMapに登録) 3auto found = mMap.find(key); 4if (found == mMap.end()) { 5 // 見つからなかった時の処理 6} else { 7 // 見つかった時の処理(foundを使うことが多い) 8}

このfound == mMap.end()が嫌いです。見つからなかったという事実を確認するだけのために、mapのインスタンスが必要になるので使いにくいのです。

「何も指していない」ポインタとしてnullptrがあるように、何も指していないiteratorって、やはりないのでしょうか?
そこそこ検索してみたけど見つられなかったので、残念ながら、なさそうです。

皆さんはfind()からちょっと離れたところで、処理したい時、どのようにしていますか?
一々インスタンスをハンドリングして、そのend()と比較してますか?
not foundフラグを別途ハンドリングしてますか?
もし、下記ようにスマートに書ける方法を構築されている方がいらっしゃったら、よろしれば方法を教えて頂けないでしょうか?

C++

1if (!found) { 2 // 見つからなかった時の処理 3} else { 4 // 見つかった時の処理(foundを使うことが多い) 5}

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

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

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

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

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

guest

回答5

0

直接的な回答ではないのですが、C++標準ライブラリ(旧STL)の "Iterator(イテレータ; 反復子)" がイケてないとする認識は結構以前からあり、改善策として "Range(レンジ; 範囲)" を用いるAPI設計が提案されています。

未来(2017年頃?)のC++標準規格策定活動の一環として、"C++ Extensions for Ranges(Range TS)"という拡張仕様(PDF n4560)が提案されており、順調にいけば未来のC++標準ライブラリの一部になるかもしれません。

で、このRange TSが導入された未来では「コンテナに入って無かったことを示す」がどう変わるのかという話ですが…、n4560を斜め読んだ限り、残念ながらまだまだ十分な合意が取られていないようです。

投稿2016/01/25 11:44

yohhoy

総合スコア6191

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

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

Chironian

2016/01/25 12:47

回答、ありがとうございます。(そして、「Faith and Brave - C++で遊ぼう」の中の人にもありがとう。日本語訳、ホントありがたいです。) なるほど、IteratorからRangeですか!! 流石に向こうの人は凄いですね。 Rangeなら見つからなかった時は空にすれば良さげですね。 当面は、必要な時に既存のiteratorを拡張する方向で考えてみます。
guest

0

一々インスタンスをハンドリングして、そのend()と比較してますか?

してます。そういうものだと思って使っているので特に嫌いとか感じたことはないです。iteratorは中身の実装を規定していないので、null値のような物は定義できないのではないでしょうか。

not foundフラグを別途ハンドリングしてますか?

存在チェックはfind()直後に行うケースがほとんどですが、離れた場所に伝達する際はそれ以外の方法は思いつきません。

投稿2016/01/25 08:51

catsforepaw

総合スコア5938

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

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

yuba

2016/01/25 09:12

そういうものだと手に覚えさせるのは悪いことではないのですが、OOP設計的にはSTLのiteratorの設計はよろしくないと思っています。イテレータパターンは、イテレータを入手したらもう元のコンテナのことを知らなくても要素にアクセスできるというのがメリットの設計であり、このイテレータが有効かどうかを判定するのに元のコンテナを必要としてしまう時点でわざわざイテレータパターンに乗せようとした意義が半減しています。別にnull値でなくとも、is_valid(found) と書けば判定できるような関数が(マクロでもテンプレートでも、いかなる形であれ)用意できていればOOPとしては十分に良い設計になっていたと言えますし、そうしたらたぶんこの質問への回答にもなっていたと思います。
catsforepaw

2016/01/25 09:38

言葉足らずだったようですね。 STLを使ってどうするのかという質問だと解釈したので、「STLはそういうものだ」ということでそう書きました。STLの設計が良くないことは私も同感ですが、設計への批判は回答としてはふさわしくないと思いましたし、そういうことはC++標準化委員会に訴えてください、というのも答えになっていませんし、自前で設計しろというのも質問の答えではないような気がします。Chironianさんなら必要であれば自前で作りますよ。
Chironian

2016/01/25 09:42

catsforepawさん、回答ありがとうございます。 私が知らないというわけではなくて、それしかないっぽいですね。 end()への挿入(追加)と有無判定で、挿入位置返却の方を取ったのだろうとは思うのですが... > null値のような物は定義できないのではないでしょうか。 ですね。たぶん概ねの実装はポインタでしょうから、無理っとやればできそうな気もしますが、非ポータブルになる以外にも何か問題が出そうな予感がするのでやらないです。
Chironian

2016/01/25 09:51

yubaさん、コメントありがとうございます。 catsforepawさん、追加コメントありがとうございます。 やっばこの点についてのSTLの仕様は良くないですよね。同じように感じている方がいてほっとします。 (実際にはポインタ・サイズになりそうですが)1ビット節約しなければ便利だったのに。 そっか、イテレータを派生して必要な情報を追加すればできるかも?
catsforepaw

2016/01/25 10:15

> イテレータを派生して必要な情報を追加すればできるかも? 手っ取り早く要件を満たすならイテレーターとフラグをpairでパックするという手もありますが、first->firstとかfirst->secondとか、なんだかややこしくなってしまいますね。 やはり目的に応じて設計した方が良さそうです。
guest

0

http://www.cplusplus.com/reference/map/map/count/

なんか間抜けな仕様なんですが、map::count()が「あれば1、なければ0」なんで、一般的なライブラリでいうcontains()的な働きをしているんですよね。とは言え、このcountでいかがでしょう。

投稿2016/01/25 06:45

yuba

総合スコア5568

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

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

Chironian

2016/01/25 06:50

おお、count()は知りませんでした。ありがとうございます。 見つかったかどうかだけの判定なら十分ですね。 ただ、すいません、書き漏れてました。見つかった場合は、その位置もハンドリングしたいのです。(質問文を編集します。)
guest

0

自己解決

結局、下記クラスを作りました。

C++

1template<class tContainer> 2class Iterator : public tContainer::iterator 3{ 4private: 5 tContainer* mContainer; 6public: 7 Iterator(typename tContainer::iterator iIterator, tContainer* iContainer) : 8 tContainer::iterator(iIterator), 9 mContainer(iContainer) 10 { } 11 12 operator bool() 13 { 14 return (mContainer->end() != *this); 15 } 16};

判定を生成直後にするか、取り出し時にするかちょっと悩ましかったのですが、通常のiteratorの使い方から外れない方が問題が起きにくいだろうと思い、このようにしました。
本当はyubaさんの言うようにis_valid()みたいな判定ができればベストですが、簡単にはできそうにないので諦めました。

使い方は下記イメージです。(まぁ、Fooクラスはなくても良いのですが、こんな使い方をしたかったので。)

C++

1class Foo 2{ 3private: 4 typedef std::map<int, std::string> MapType; 5 MapType mMap; 6 7public: 8 (中略) 9 Iterator<MapType> find(int iKey) 10 { 11 return Iterator<MapType>(mMap.find(iKey), &mMap); 12 } 13}; 14 15int main() 16{ 17 Foo wFoo; 18 (中略) 19 auto found = wFoo.find(100); 20 if (!found) { 21 // 見つからなかった時の処理 22 } else { 23 // 見つかった時の処理 24 } 25 26 return 0; 27}

投稿2016/01/25 14:18

編集2016/01/25 16:12
Chironian

総合スコア23272

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

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

0

見つかった場合に位置を取得したいのであれば、特に今のままでも煩わしさは無いのでは無いでしょうか。
例えばMapにキーが存在するか確認するメソッドが存在するJavaであれば、次の様に書きます。

java

1if (! xxMap.containsKey(key)) { 2 // 見つからない 3} else { 4 // 見つかった 5 val = xxMap.get(key) 6}

いきなりgetメソッドを使用しないのは、キーと関連付けた値がNULLの要素が存在する場合と見分けるためです。
質問内容だと、キーの存在確認と取得を一度に行いたいということになるので、その場合は「見つからない場合は例外を送出」とでもしなければならなくなると思います。

投稿2016/01/25 07:21

shanxia

総合スコア1038

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

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

Chironian

2016/01/25 07:46

すいません。Javaは知らないのですが、C++のSTLではキーの存在確認と取得を一度に行えるのですよ。 原則、見つかった要素へのポインタが返ってきますが、もし、見つからなかったらNULLが返るイメージです。 ただ、実際に返るのはNULLではなくて、見つかったかどうかの判定が面倒な形式で返ってくるのです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問