为什么std::string::substr抛出异常而不是返回空字符串?

8
我一直在思考关于std::stringsubstr(pos, len)方法设计背后的原理。它仍然不合理,所以我决定向专家请教。如果pos参数超过字符串长度加一,该函数将抛出一个std::out_of_range异常。有时这可能会很不方便(甚至让人烦恼),但我的真正关注点是一致性和最小惊奇原则。结果发现,子字符串的“结束”位置pos+len可以超过字符串长度加一。对于开头不允许这样做,而对于结尾允许这样做,这种感觉不一致。对我来说,允许结尾做到这一点暗示了以下解释:

返回所有位置pos <= i < pos+len的字符

然而,如果pos的值超过字符串长度,那么我希望函数返回一个空字符串,而不是抛出异常。顺便提一下,如果按照这种解释,允许pos的负值(如果它具有带符号类型)甚至是有意义的。
这让我有以下几个问题:
  • 您认为这种设计是否合乎逻辑?是否合理?您有解决这种不一致性的满意方法吗? 我能想到的唯一可能的解释是与空终止字符串兼容。对于空终止,指定的长度超过结尾并不重要,而从空字符之后开始则是内存错误。然而,std::string没有空终止,而是跟踪字符串的长度。如果这是真正的原因,那么我个人认为这是一个非常糟糕的原因。
  • 在性能方面有什么优势吗?我会感到惊讶。
  • 我是否忽略了可用性方面的优点?也许是与其他函数(如find)结合使用的标准习语或用例?在这里,我印象中返回一个空字符串有潜力简化一些代码。
  • 有没有办法在将来更改substr的行为?我想答案是否定的,因为默默地破坏现有代码比接受这种曲折要糟糕得多...

1
它使得说“从pos到结尾的子字符串”变得容易,这并不是一个罕见的操作。将第二个参数视为返回的子字符串中字符数的上限。 - T.C.
1
我一直说,当违反合约(先决条件)时抛出异常是不好的设计,即使异常是合约的一部分。调用者该怎么办,捕获异常并使用pos/=2循环调用函数直到停止抛出?最好使用有用的消息(ex:“清洗您的输入!”)进行断言失败。但是,STL是写于许多年前,也许当时人们并不知道更好的方法。现在,我们必须接受这种行为。 - KABoissonneault
3
你从哪里得到的信息说 substr 会在 pos > length + 1 时抛出异常?标准规定如果 pos > size() 则会抛出 out_of_range 异常。 - NathanOliver
你为什么认为std::string在其实现中 不是 以空字符结尾的? - Ternvein
@Ternvein 我知道它在实现中是以空字符结尾的,这样c_str就可以在不复制的情况下工作,但我认为至少在C++03中你不能依赖于此。真正的问题是'\0'是一个合法的字符在std::string中,因为长度不是由空字符确定的。因此,我认为依赖于空字符终止的接口对于std::string来说是不自然的。 - tglas
显示剩余3条评论
1个回答

3
这个问题实际上很主观,但是我会逐点回答它。
  • 这个设计在你看来合乎逻辑吗?显而易见的? 在我看来似乎很合理。也许这个观点来自于strncmp类型的函数,但是通过这种设计,您可以将缓冲区长度作为len参数传递,并且它会正常工作。但是,如果您试图访问位于字符串边界之外的子字符串,那么您可能会忽略一些简单的健全性检查。并且std::string的内部实现并不重要。
  • 在性能方面有优势吗? 我认为这不是原因。
  • 我是否忽视了可用性方面的优势? 可能,看看第一点。
  • 有没有改变将来substr行为的方法? 标准中定义了在pos超过size()时抛出异常,所以很可能没有。

我的观点是:这个异常(尽管我更喜欢从不使用它们)使您注意到代码缺少一些基本的健全性检查,例如访问其边界之外的缓冲区。相同的设计用于像at()这样的函数和许多其他函数中。


谢谢你的回答。我认为从末尾开始是否比结束超出末尾更基本的健全性检查是有争议的,但正如你所说,这只是因为我们已经习惯了strncmp和它的伙伴们(包括std::string::substr)。我的最爱例子size_t pos = s.find(' '); string cmd = s.substr(0, pos); string args = s.substr(pos+1)是一个合理的用例,我更喜欢空字符串而不是异常,而没有错过一个“基本”的健全性检查(没有参数也可以)。在这里使用缓冲区大小作为len很方便,同样适用于pos - tglas
你可以编写一些围绕 std::string 的类,使其按照你的意愿工作。 - Ternvein
1
其实这就是我所做的(一个简单的免费函数),但它让我很烦恼 :) - tglas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接