为什么C ++标准算法“count”会返回difference_type而不是size_t?

时间:2011-09-21 18:53:25

标签: c++ std

为什么std::count的返回类型是迭代器的difference_type(通常是ptrdiff_t)。

由于计数永远不会是负数,因此技术上size_t 是不是正确的选择?如果计数超出ptrdiff_t的范围怎么办,因为数组的理论可能大小可以是size_t


编辑:到目前为止,关于函数返回ptrdiff_t的原因还没有合适的答案。从下面的答案中收集的一些解释是返回类型是iterator_traits<InputIterator>::difference_type,它是通用的,可以是任何东西。直到那时它才有意义。有些情况下,计数可能超过size_t。但是,对于标准迭代器而不是typedef ptrdiff_t iterator_traits<InputIterator>::difference_type,返回类型为typedef size_t iterator_traits<InputIterator>::difference_type的原因仍然没有意义。

7 个答案:

答案 0 :(得分:14)

std::count()算法依赖于迭代器类型来定义一个足以表示范围的任何大小的整数类型。容器的可能实现包括文件和网络流等。不能保证整个范围一次适合进程的地址空间,因此std::size_t可能太小。

标准std::iterator_traits<>提供的唯一整数类型是std::iterator_traits<>::difference_type,它适用于表示两个迭代器之间的“距离”。对于实现为(包装)指针的迭代器,此类型为std::ptrdiff_t

答案 1 :(得分:7)

size_t在技术上不是正确的选择,因为它可能不够大。允许迭代器迭代比内存中任何对象更大的“东西” - 例如磁盘上的文件。当他们这样做时,迭代器可以定义一个大于size_t的类型difference_type,如果有的话。

difference_type需要签名,因为在std::count以外的上下文中,它表示迭代器在两个方向上的偏移。对于随机访问迭代器,即使it + difference为负数,difference也是一个非常明智的操作。

iterator_traits不提供无符号类型。也许它应该,但鉴于它不是iterator_traits<InputIterator>::difference_type是最好的类型。

迭代器是否应提供无符号类型的问题可能与编码样式的大量冲突有关,无论是否应使用无符号类型进行计数。我不打算在这里重现那个论点,你可以查一查。 ptrdiff_t确实存在一个弱点,即在某些系统上它不能代表所有有效的指针差异,因此也无法代表std::count的所有预期结果。

据我所知,即使在C ++ 03中,该标准实际上禁止这一点,也许是偶然的。 5.7 / 6谈论减法可能溢出ptrdiff_t,就像C一样。但是表32(分配器要求)说X::difference_type可以表示任意两个指针之间的差异,并且std::allocator保证使用ptrdiff_t作为其difference_type(20.1.5 / 4)。 C ++ 11是类似的。因此标准的一部分认为指针减法可能会溢出ptrdiff_t,标准的另一部分则认为它不能。{/ p>

std::count可能是在与分配器要求相同(可能有缺陷)的假设下设计的,ptrdiff_t足以表达任何对象的大小,并且(通常)是迭代器的{{1}可以表示任意两个迭代器之间的迭代计数。

答案 2 :(得分:5)

返回类型为typename iterator_traits<InputIterator>::difference_type,在此特定情况下恰好为ptrdiff_t

大概选择difference_type是因为范围中匹配元素的最大数量是迭代器差异last - first

答案 3 :(得分:1)

原来std::count是:

template <class InputIterator, class EqualityComparable, class Size>
void count(InputIterator first, InputIterator last, 
           const EqualityComparable& value,
           Size& n);

在该函数中Size是一个模板参数。它可以是你喜欢的任何东西,你有责任确保它是正确的。它可能是您平台上最长的类型。

我怀疑当新形式时:

template <class InputIterator, class EqualityComparable>
iterator_traits<InputIterator>::difference_type
count(InputIterator first, InputIterator last, 
      const EqualityComparable& value);

已添加iterator_traits已存在,因此重新使用现有类型的优势在于,与{{1}中添加另一个typedef相比,它保持对标准的更改小且本地化}}

这样做,使用iterator_traits而不是简单地使用iterator_traits意味着每个可能的迭代器都可以选择指定std::size_type应该返回的确切类型。这包括从网络或磁盘读取的自定义迭代器,它可以使用比std::countptrdiff_t和朋友大得多的东西。 (如果需要,可以成为某种“BigInt”)。这也意味着用户不负责推断使用的相应类型,这可能很棘手,正是因为自定义迭代器的可能性。

答案 4 :(得分:0)

即使计数不能为负数,返回类型也会指定为iterator_traits<InputIterator>::difference_type,两个迭代器之间的差异可能为负数。

答案 5 :(得分:0)

如果迭代器是一个数组,则意味着结果在数组的范围内。

对于这个特定的算法,我无法想到一个有趣的原因。对于使用它作为组件的人来说,它可能很有趣。

该页面确实表示会执行等效的。因此对于数组的情况,它可能会做类似直接指针差异的事情。如果它适用的话,这将是一个非常快速的专业化。

答案 6 :(得分:0)

difference_type通常表示适合表示数组或类似距离的类型。以下措辞来自分配器要求,但只要标准谈到difference_type,它就意味着相同的概念:

  

一种类型,可以表示任何两个指针之间的差异   分配模型

这种自然类型是ptrdiff_t。

对于size_type,它说:

  

一种类型,可以表示最大对象的大小   分配模型。

这里的自然类型是size_t。

现在,对于范围(或数组)中的任何元素的计数,至少需要适合指定差异last-first的类型。选择那一个似乎是最自然的。