`size_t`总是`vector<int>::size_type`或任何其他容器类型的别名吗?

5

让我们来举一个最简单的例子:

公式 1:

std::vector<int> vec;
// add 10E11 elements
for(std::size_t n = 0; n < vec.size(); ++n)
    // ...

表述2:

std::vector<int> vec;
// add 10E11 elements
for(std::vector<int>::size_type n = 0; n < vec.size(); ++n)
    // ...

自然地,`unsigned int`或任何不合适的数据类型在这里都不起作用,我们必须编译x64。我的问题是:是否有任何情况下第一种公式会导致问题,或者我们可以安全地总是使用这种更短的表示法?如果有类似的设置(x86、任何其他容器、`size_type`的其他应用),我也很感兴趣。

1
如果你使用迭代器和auto,就不会有这个问题了...;-) - DevSolar
1
有些情况下,我需要跟踪索引计数或在循环中更改迭代索引。 - IceFire
典型的定义是,size_t 可以存储任何类型(包括数组)的理论上可能存在的最大对象的大小。 - rcgldr
@IceFire:索引计数为it-vec.begin(),更改迭代器为it += x。但这只是吹毛求疵,我理解你的观点。我希望你也能理解我的观点。;-) - DevSolar
1
一个丑陋的hack来确保类型正确是 auto n = vec.size() * 0; - M.M
@DevSolar 我有!问题是当使用迭代器但推断索引计数时,我们是否仍然具有性能优势。我怀疑有,但尚未搜索基准测试。 - IceFire
3个回答

4

std::vector保证指针是其整个序列的有效迭代器,因为vector::data返回“一个指针,使得[data(),data()+size())是一个有效范围”。这就是指针加法,它在std::ptrdiff_t上定义,这是std::size_t的有符号版本。

此外,[iterator.requirements.general]/6适用:

对于整数值n和可解引用迭代器值a(a + n)*(a + n)等效于*(addressof(*a) + n)

vector::size_type可能比std::size_t更窄,例如在64位系统上为32位。但这不是我会担心的事情。


因此,要明确你的发现:标准意味着 sizeof(std::size_t) >= sizeof(std::vector<T>::size_type) 对于任何 T 和任何分配器都成立,因此 size_t 始终是安全的,但在某些情况下可能在内存方面效率低下。 - IceFire
1
@IceFire 没错。实际上 size_type 与具有分配器感知接口是无关的,而且在分配器上看到任何依赖关系会令人惊讶。对于任何容器来说,它的宽度(至少)与 make_unsigned_t<Container::iterator::difference_type> 一样广泛。 - Potatoswatter
嗯,实际上并不那么令人惊讶,这只是对花哨指针的支持。问题在于标准对花哨指针有相当多的限制。 - Potatoswatter

3
虽然在所有常见的实现中情况都是如此,但标准对此并不做出保证。保证的是 std::vector<T>::size_type 是一个由实现定义的无符号整数类型。
参考文献:
23.2.1 通用容器要求 [container.requirements.general]

X::size_type 是一种无符号整数类型

以及 23.3.6.1 向量类模板概述 [vector.overview] §2
typedef implementation-defined size_type; // see 23.2

0

虽然std::vector::size_type通常是std::size_t,但并不保证一定是这样。更安全的做法是使用

for(std::vector<int>::size_type n = 0; n < vec.size(); ++n)

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