なぜ `std :: string :: find()`は失敗時に終了イテレータを返さないのですか?


29

の動作がstd::string::find標準のC ++コンテナと一致しないことがわかりました。

例えば

std::map<int, int> myMap = {{1, 2}};
auto it = myMap.find(10);  // it == myMap.end()

しかし、ひもについては、

std::string myStr = "hello";
auto it = myStr.find('!');  // it == std::string::npos

代わりに失敗したmyStr.find('!')リターンをすべきではないmyStr.end()std::string::nposですか?

std::stringは他のコンテナと比較するとやや特殊なので、これには何らかの理由があるのか​​と思います。(驚いたことに、私はこれをどこかで疑う人を見つけることができませんでした)。


5
「ホットドッグが4パックになり、ホットドッグバンが6パックになるのはなぜですか?」まあ、それは世界が
起こっ


私見、この動作の理由は、std::string内部的には(メモリに関して)安価な要素である文字で構成されているためです。そして、さらに、キャラクターがstd::string含むことができる唯一のタイプです。一方、std::mapより複雑な要素で構成されています。また、の仕様はstd::map::find要素を見つけることを想定していることを示し、の仕様はstd::string::find位置を見つけることがそのタスクであることを示しています。
NutCracker

マップの場合、nposイテレーターを使用できないため、終了イテレーターが使用されます。文字列の場合、nposを使用できるので、なぜか:)
LF

回答:


28

まず、std::stringインターフェースが肥大化し、一貫性がないことがよく知られています。このトピックについては、Herb SutterのGotw84を参照してください。しかし、それでも、std::string::findインデックスを返すのには理由がありますstd::string::substr。この便利なメンバー関数は、インデックスで動作します。

const std::string src = "abcdefghijk";

std::cout << src.substr(2, 5) << "\n";

substr文字列へのイテレータを受け入れるように実装することもできますが、std::string使用不能で直感に反する大きなクレームを待つ必要はありません。それがstd::string::substrインデックスを受け入れる場合'd'、この部分文字列から始まるすべてを出力するために、上記の入力文字列で最初に出現するインデックスをどのように見つけますか?

const auto it = src.find('d'); // imagine this returns an iterator

std::cout << src.substr(std::distance(src.cbegin(), it));

これもあなたが望むものではないかもしれません。したがってstd::string::find、インデックスを返すことができます。

const std::string extracted = src.substr(src.find('d'));

イテレータを操作する場合は、を使用します<algorithm>。彼らはあなたに上記を可能にします

auto it = std::find(src.cbegin(), src.cend(), 'd');

std::copy(it, src.cend(), std::ostream_iterator<char>(std::cout));

4
いい視点ね。ただし、イテレータをstd::string::find返すsize()代わりにnpos、との互換性を維持する代わりに、を返すこともできますが、substrいくつかの余分な問題を回避することもできます。
エレノン

1
@erenonたぶん、std::string::substr2番目のインデックスのデフォルトパラメータ(npos)を使用して、「ここから最後まで開始」のケースをすでにカバーしています。戻るのsize()もまた混乱するだろうし、文字通りの歩哨を持ってnposいる方が良い選択かもしれませんね!?
lubgr

@lubgrしかし、もしstd::string::findイテレータを返すなら、std::string::substrおそらく開始位置のイテレータも受け入れます。この代替世界では、findを使用した例は、どちらの場合も同じように見えます。
マティアスウォリン

@MattiasWallin良い点。ただしstd::string::substr、イテレーター引数を使用すると、別のUBケース(インデックスまたはイテレーターでも同様に発生する過去のシナリオ以外)の扉が開きます。別の文字列を参照するイテレーターを渡します。
lubgr

3

これは、std::string2つのインターフェースがあるためです。

  • すべてのコンテナにある一般的なイテレータベースのインターフェース
  • std::string特定のインデックスベースのインターフェイス

std::string::findインデックスベースのインターフェイスの一部であるため、インデックスを返します。

std::find一般的な反復子ベースのインターフェースを使用するために使用します。

std::vector<char>インデックスベースのインターフェースが必要ない場合に使用します(これを行わないでください)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.