C++ std库中`at()`索引函数的真实用例?

Mar*_* Ba 22 c++ collections standard-library

C++的容器vector,deque... at(index)除了operator[index]访问容器元素外,还提供访问器功能.

这个成员函数和成员运算符函数operator []之间的区别在于deque :: at信号,如果请求的位置超出范围,则抛出out_of_range异常.

我从来没有在代码中需要这个函数,因为在我的C++代码中访问可能超出范围的元素是没有意义的.始终编​​写代码以访问正确的索引(或者在索引无法匹配的情况下产生有意义的错误/异常.)

我会对真实世界的例子(可能来自一些开源项目,因为它会添加一些上下文)感兴趣,at()在生产代码中使用的地方.

也许有人可以举例说明使用at()有意义的算法问题.

注:我已经用它最近在一些单元测试代码,其中加入指数校验码不被认为值得的麻烦和抛出的异常out_of_range at()被认为是足够的信息+背景的情况下,测试中断.

注意:关于ildjarn的这个答案 - 我不想就此开始讨论或评论.我在"积极"的兴趣所在,那就是具体的例子被使用.谢谢.

pax*_*blo 13

好吧,当你不控制正在使用的索引时(例如,如果它是由代码的客户端传入的),你应该检查它是否在手动范围内,或者用于at报告异常(你是可以使用您自己的错误报告捕获并通知调用者,或者只是向上传播标准异常).

换句话说,被调用函数的责任是检查输入参数,但是它是使用if语句明确地这样做还是通过使用at而不是隐式地[]进行辩论.如果我要做的就是抛出一个out_of_range异常(如果传入的索引大于或等于集合的大小),我想我会让它at自己做一些编码.

静默地传回坏数据几乎不是最好的解决方案.简单地将x [7]传回四元素整数组的问题是调用者认为它是有效的零.这是不是这种情况.

  • 我不得不同意这个,@ musiKk.验证是验证它是在您的代码中检查还是从较低级别抛出.我不忽略文件操作中的异常,然后在每次操作后手动检查状态标志 - 只是捕获ios_base :: failure并将错误处理隔离到`catch`位更容易. (5认同)
  • 我认为基于异常处理的输入验证是个坏主意.始终明确检查. (3认同)

ild*_*arn 7

在我看来,at()是一个100%无用的成员函数.仅在标准库容器的有效范围内访问是使用该容器的前提条件,并且应该使用而不是通过抛出异常来处理对任何先决条件的违反assert.的存在at(),绝不可帮助容器维持其前提条件/不变量,而实际上只有通过使适当的边界检查访问似乎没有混淆的问题一个先决条件.

即,抛出异常最终只能由程序员错误引起的事情是愚蠢的.有关更详细的解释,请参阅此主题,特别是D. Abrahams的帖子; 虽然它可能很长,但绝对值得阅读:comp.lang.c ++.moderated:Exceptions.

编辑:为了回应OP的补充说明,我说的是根据我对C++的经验 - 专业,开源和其他 - 我从来没有遇到过使用标准容器的问题at(),并且保持这一点它实际上没有在生产代码中使用.进一步的评论或阐述仅仅是为了理解为什么我认为是这种情况.

  • @ildjarn:我同意这很令人困惑,虽然只是非常轻微,C++程序员需要足够聪明才能记住这些奇怪的东西.这是一个编码风格的问题,你是否选择在无法访问向量的情况下抛出异常,因为你没有入境索引.编写`if((i> = 0)和(i <vec.size())返回vec [i];或者抛出out_of_rage();`而不是编写`,这在道德上更精细,更难以维护,也不简单.返回vec.at(i);`.所以*如果*你想要一个例外,`at`是有用的.如果你不这样做,那就不是. (7认同)
  • 我要离开这里的讨论,因为它正在解决这个问题.我的最后一点是,您绝不应该假设您的调用者正在传递有效数据.期.我现在闭嘴了:-) (4认同)
  • 我的观点是,您不知道进入您的函数的数据的最终来源.这可能是程序员出错的原因,可能是一个错误的错误,或者程序员应该抓住许多其他原因.程序员还无法控制(用户,文件等).但是,程序员使用您的代码无能为力,绝不能免除您自己编写代码的责任.无论是检查还是异常,我都不在乎.但我不认为断言就是答案. (2认同)
  • @paxdiablo:**那个**我们当然可以同意,如果不是如何处理无效数据.;-D (2认同)

Ton*_*roy 6

我一直认为at()有用的一个用例是促进解析复杂的用户输入.例如,在分析C++代码时,我发现自己在检查语法结构时会移动一系列词法标记.逻辑通常类似于"如果此标记是标识符,并且下一个标记是等于那么它应该是一个赋值,因此请提前扫描分号标记以建立表达式的标记范围".at()在这样的代码中使用意味着您可以轻松地表达与当前点相关的偏差,ala:

if (tokens.at(i) == Switch)
{
    if (tokens.at(++i) != Left_Parentheses)
        // throw or return to say input program's broken...
    if (tokens.at(++i) == ...)
        ...
}
Run Code Online (Sandbox Code Playgroud)

每当您尝试解析无效程序时,都会收到异常.增加位置发生在整个代码的许多地方,不断重新调整大小将是一场噩梦(冗长且极易出错),因为在这种情况下,你只能意识到程序需要多大才能有效.应用语法规则.at()与功能等效替代方案相比,此处使用简洁,强大,直观且性能合理.

FWIW - 快速搜索我们的生产代码(200k行,大部分在我加入团队之前编写)发现了十几种用法at().


Mat*_* M. 5

我的理由是:为什么不使用它

除非你是在你的应用程序的性能关键部分,你应该总是青睐std::out_of_range未定义行为,至少这是我的信条.

在实践中,我通常会将我正在处理的所有代码转换为使用已检查的访问.对于大多数代码来说,性能损失是不可见的,至少我有一个很好的报告,其中包含有关当前执行上下文(在catch(std::exception const&)根级别生成)的信息,而不是内存损坏,这会使我的代码失败一段时间以后(或更糟糕的是,看起来它有效).

我同意输入应该首先得到验证,我同意你应该事先检查你的访问权限...但是如果你忘记了或有错误,最好有一个at().

使用[]而不是at()像携带装载的枪没有/与(分别)口袋里的安全.你可以忘记戴上它,但愿意将其删除?那是疯了.