为什么`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个容器中?” 好吧,这就是世界的样子
-bartop


恕我直言,这种行为的原因是std::string内部由字符组成,这些字符是廉价的元素(就内存而言)。而且,字符是唯一std::string可以包含的类型。另一方面,std::map由更复杂的元素组成。另外,的规范std::map::find说应该找到一个元素,而的规范std::string::find说它的任务是找到位置。
NutCracker

对于map,您不能有npos迭代器,因此要使用end迭代器。对于字符串,我们可以使用npos,所以为什么不:)
LF

Answers:


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,同时还避免了额外的麻烦。
erenon

1
@erenon可能,但是std::string::substr已经用第二个索引(npos)的默认参数涵盖了“从这里开始直到结束”的情况。我想返回size()也将造成混乱,让字面量的哨兵像是npos更好的选择?
lubgr

@lubgr但是如果std::string::find返回一个迭代器,则std::string::substr可能还会接受一个迭代器作为起始位置。在这个备用环境中,带有find的示例在两种情况下看起来都是相同的。
Mattias Wallin

@MattiasWallin好点。但是std::string::substr使用迭代器参数可以为进一步的UB案例(除了在索引或迭代器中同样可能发生的过去情形)敞开大门:传递引用另一个字符串的迭代器。
lubgr

3

这是因为std::string有两个接口:

  • 在所有容器上都可以找到基于常规迭代器的接口
  • 基于std::string特定索引的界面

std::string::find是基于索引的接口的一部分,因此返回索引。

使用std::find使用一般的基于迭代器接口。

使用std::vector<char>,如果你不希望指数基于接口(不这样做)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.