我最近很恼火地发现在大海捞针中找不到针头时会string::find返回string::npos.这使得以下看似优雅的代码编译但抛出超出范围的异常:
s.erase(s.find('#')); // erase everything after a # if one exists
Run Code Online (Sandbox Code Playgroud)
如果失败后find返回s.length(),它将正常工作.相反,你必须这样做
auto pos = s.find('#');
if (pos != s.npos)
s.erase(pos);
Run Code Online (Sandbox Code Playgroud)
std::find如果找不到该项,这也与返回结束迭代器不一致.
我知道标准人都非常聪明,所以我相信他们并不是偶然想出来的.它必须在其他地方给我一些优雅,我没有看到.这有什么好理由?
你的问题实际上是双重的:
1)为什么它
std::string有自己的find函数返回std::size_t值而不是迭代器?
这主要是由于std::string与标准库的其余部分分开开发的事实.仅在最近的标准中,它已被其他模板(例如iostream)所接受.因此,当它被添加到标准中时,它添加了一些函数,但它的原始功能几乎保持原样(例外是写入时复制的常见实现,这在C++中被禁止) 11标准).这种方式主要是为了向后兼容.
根据你的问题,为什么它是这样开始的:原始string.h是一个非常薄的包装几个C字符串函数.在复制构造函数中看到strlen用作返回值length()或strcpy在复制构造函数中使用它并不罕见.没有强制使用这些函数的要求,因此最终实现者开始做一些有趣的事情(例如,写时复制,非连续的内存块),但是它们使接口保持相同以保持向后兼容性.在添加功能的同时,没有从界面中删除任何公共功能.因此,您可以跟踪设计决策,使用指针和函数参数的长度回到它只是C函数的包装器的日子.
2)如何在字符串上写一个擦除序列而不必检查返回值?
这可以通过使用find-erase惯用语来完成,但不能使用std::stringfind函数:
s.erase(std::find(s.begin(), s.end(), '#'), s.end());
Run Code Online (Sandbox Code Playgroud)
使用std::string::npos使结果成为一个不变的表达式std::string::length().因为npos不适合作为迭代器,所以有一个常量表达式的值,例如,它可以用作参数a的默认值std::string::size_type.
另一个原因是std::basic_string在将STL添加到C++标准库之前将基本接口放在一起(至少,存在接口的一部分).原始接口基本上是一个不可变的字符串,我认为它不支持字符串本身的任何变异.
小智 2
我对此不确定:原始的 std::sting (STL) 不需要连续存储数据。因此,在操作失败时返回 size() 将是一种开销(如果未存储大小)。在 c++11 中,字符串是连续的,我同意你的批评。