【问题标题】:Is std::vector::size() allowed to require non-trivial computations? When would it make sense?是否允许 std::vector::size() 需要非平凡的计算?什么时候有意义?
【发布时间】:2011-04-18 11:02:23
【问题描述】:

我正在查看一段代码,看到一个类,其中 std::vector 存储为成员变量,而 std::vector 的大小存储为单独的成员变量。 std::vector 及其大小的“存储副本”在包含对象的生命周期内永远不会改变,并且 cmets 表示大小是单独存储的“为了方便和实现每次都计算大小的情况 em>”。

我的第一反应是“WT*?提取std::vectors 大小不应该总是微不足道的吗?”

现在我已经仔细阅读了 C++ 标准的 23.2.4 并且看不到任何说明是否首先允许这样的实现,我无法想象为什么有必要以这种方式实现 std::vector它当前的大小需要不平凡的计算。

是否允许std::vector::size() 需要一些重要的操作?什么时候进行这样的实现才有意义?

【问题讨论】:

  • 我的评估是;我不在乎我们使用哪种实现,请随意将其换成其他任何东西。但是,如果您依赖于它是 std::vector 的事实,那么它可能总是被内联,所以是的。
  • size 是一个 const 函数,这意味着它不可能调用任何非 const 函数,例如分配内存或锁定。因此,它可能进行的任何重要计算无论如何都是微不足道的。很可能,它只是返回 size 成员的值,但即使它要进行一些“复杂的数学运算”,例如将几个子缓冲区的长度相加(顺便说一句,这对于向量是不合法的,因为数据是保证是连续的),开销仍然是微不足道的。
  • @Damon - 它仍然可以计算生命的终极问题宇宙和每个元素的一切,并将总和除以 42 得到大小。我同意有一个合乎逻辑的实现。但遗憾的是,这并不能阻止某些人编写太“聪明”而我们无法理解的代码。

标签: c++ stl vector std


【解决方案1】:

C++03 在第 23.1 节中的表 65 中说,size() 应该具有恒定的复杂性。 (在 C++0x 中,这对所有容器都是必需的。)您将很难找到不是 std::vector<> 的地方。

通常,正如史蒂夫所说,这只是两个指针之间的区别,一个简单的操作。

【讨论】:

  • 是吗?我从来不喜欢也不理解那个注释,“应该有恒定的复杂性”。不过,我同意它“应该”被视为一项要求;-)
  • @Steve:是的,这是不必要的混乱,而且 IIRC 的措辞很糟糕。
  • @GMan:正如AProgrammer 所说(在已删除的答案中),list 和采用迭代器对的splice 版本存在问题。正如您所说,该标准的措辞很糟糕,因此会造成混淆 w.r.t vector
  • 这里定义了“应该”的含义:isotc.iso.org/livelink/…。参见“附件 H”,表 H.2“建议”。
  • @Johannes 来救援!那么有道理:这就是对splice 的宽大处理的来源。
【解决方案2】:

我猜您对“琐碎”的定义与代码作者的定义不符。

如果size 没有被存储,我希望beginend 被存储,size 被计算为两者的差,并且该代码被内联。所以我们基本上说的是两次(附近)内存访问和一次减法,而不是一次内存访问。

对于大多数实际目的,这两者都是微不足道的,如果标准库作者认为该计算的结果不值得缓存,那么我个人很乐意接受他们的意见。但该代码注释的作者可能不这么认为。

IIRC 标准在某处说size“应该”是 O(1),不确定是在文本中是针对序列还是针对容器。我不认为它在任何地方都指定它必须vector。但是,即使我们认为这是一个非要求,这里也存在一个基本的 QOI 问题 - 我到底在做什么以牺牲正常实现为代价来优化我的代码以实现如此糟糕的实现?

如果有人使用这样的实现,大概是因为他们希望他们的代码运行缓慢。否则我有什么资格评判? ;-)

代码作者也可能使用了许多end-begin 实现进行了概要分析,并通过缓存大小测量了显着的改进。但我认为这不太可能是作者对他们的代码需要处理的最坏情况过于悲观。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多