为什么标准同时有seekpos()和seekoff()?

Why does the standard have both seekpos() and seekoff()?

标题几乎说明了一切。

我正在实现自己的 streambuf class,我想知道委员会在决定同时包含 std::streambuf::seekpos()std::streambuf::seekoff() 时是怎么想的(对于相关的 pubseekpos()pubseekoff()).

在我看来,seekpos(x) 在我能想象到的任何情况下都应该具有与 seekoff(x, std::ios::beg) 相同的效果。但也许我的想象力不够好。那么,当 seekpos 完全包含在 seekoff 中时,您为什么要同时拥有两者?或者至少,为什么不让前者的默认实现简单地调用后者?

如果答案是 "because that is what the STL did" 或类似的,那么假设我问的是 STL 或类似的基本原理。

(我意识到 seekpos 的第一个参数是 streamposseekoff 的第一个参数是 streamoff。但请注意 27.4.3.2 [ lib.fpos.operations] 要求流位置可以转换为流偏移并返回,所以我认为这不是答案。)

I am wondering what the Committee was thinking when they decided to include both std::streambuf::seekpos() and std::streambuf::seekoff() (and similarly for the related pubseekpos() and pubseekoff()).

streampos 表示从流开头算起的绝对位置。因此,seekpos() 寻找特定位置。

streamoff 表示距给定位置的偏移量。因此,seekoff() 可以相对于任何给定位置进行搜索。这就是为什么它有一个 dir 参数来指定如何解释偏移量 - 与流的 beginning/end 的偏移量,或相对于流的当前位置的偏移量。

不同的语义,所以提供不同的功能。

It seems to me that seekpos(x) should have the same effect as seekoff(x, std::ios::beg) in every circumstance I can imagine.

从逻辑上讲,这就是它的作用,是的。尽管可以优化实际实现,例如通过考虑当前位置来最小化涉及的物理查找量,尤其是对于文件流。让 OS 一直回到文件开头只是为了再次向前移动并不总是有效的。

So, why would you want to have both when seekpos is completely subsumed by seekoff?

因为有时直接寻找特定位置是有意义的,而有时寻找相对偏移量是有意义的。这取决于具体情况。两种情况都有其用途。

So, why would you want to have both when seekpos is completely subsumed by seekoff?

方便。取决于调用代码的设计,它是否跟踪绝对位置或相对偏移量。

Or at least, why would you not make the default implementation of the former simply invoke the latter?

有可能。它是实现定义的。

But note that 27.4.3.2 [lib.fpos.operations] requires that stream positions be convertible to stream offsets and back, so I do not think this is the answer.)

Convertible 是这里的关键词。仅仅因为它们可以相互转换并不意味着它们在语义上等同

两个函数使用不同的参数:

  • seekpos() positions you in a stream, using an absolute streampos 位置
  • seekoff() positions you in a stream, using a relative streamoff 与给定位置(开始、当前或结束)相比的偏移量

你完全正确:它们可以双向转换。但是有一个细微的语义差异:

  • streampos 直观地与固定位置相关,而 streamoff 与文件中的运动有某种关系。
  • 从另一个 streampos 中减去一个 streampos 得到 streamoff.
  • streampos 添加到 streamoff 会生成一个新的 streampos
  • 由于易于转换,其他组合也有效,但需要更加小心。
  • wide 流使用 wstreampos 作为位置(因为显然 wchar_t 中的位置与 char 流中的位置具有不同的含义)。
  • 但宽流使用 streamoff 进行运动,因为它是相对于位置的相对运动。

如果您使用模板编程,这些语义差异在特征中更加明确:S::pos_typeS::off_type

真实来源

不管这些语义细节如何,这种差异的真正起源是历史性的,并且与 C 标准库 相关,正如 Plauger 先生在 this article 中所解释的那样:

  • streamoff 是根据 C 标准库的 fseek()ftell() 设计的,它们使用 long 进行定位。
  • streampos 用于使用 fpos_t 参数的 fgetpos()fsetpos()

我可以验证 C89 标准中已经存在这种情况。由于这些功能在最初的 K&R 中都没有被提及,也许 UNIX 历史学家可以告诉更多关于更遥远的路径。

This paper 解释了标准委员会最近的想法。看来历史差异受到质疑,是否真的还需要它已经不清楚了。