【发布时间】:2015-11-02 02:20:42
【问题描述】:
我很难在 size_t 和 ptrdiff_t 之间选择索引类型,它应该能够存储负值。
确切地说,在我的代码中,我需要实现一个数组。我将它的长度(在构造函数中)作为size_t 的类型接收,当我重载[] operator 时,我需要索引类型为ptrdiff_t(而不是size_t),因为我想允许负索引,如本例所示:
std::size_t length = 50;
MyVector<int> vec(length);
vec[0] = 10;
MyVector<int> vec2 = vec+1;
std::cout << vec2[-1] << std::endl; //should print 10
上述设计产生的问题是可用索引的范围受到ptrdiff_t的最大值的限制,并且在某些机器中,这个上限小于size_t的最大值。
即std::numeric_limits<std::ptrdiff_t>::max() < std::numeric_limits<std::size_t>::max()
因此,问题在于用户可能会创建一个大小大于ptrdiff_t 最大值的数组(当然,仍在size_t 的范围内),但他不会无法访问数组中超过最大值ptrdiff_t 的元素,因为它们的索引会溢出到负数。在我的机器上,这将可用索引减半! (因为size_t和ptrdiff_t都是64位,但一个是unsigned,另一个是signed)
以下是我想出的解决方案,但遗憾的是它们都不是完美的:
-
在构造函数中,接受
ptrdiff_t类型的长度而不是size_t,并添加一个检查以验证给定长度是否为负数。优点:它解决了这个问题,因为现在我将无法访问数组中的所有元素,并且仍然允许负索引。
缺点:它限制了数组的最大可能长度。 (例如,就像我之前说的,在我的机器中它减半)
-
保持原样,但在
[] operator中,将索引转换为size_t类型,并利用负值会溢出的事实。即给给定的索引,添加我们当前指向的元素和
例如,在我之前的示例中,由于 vec2 指向数组中的 second 元素,
[] operator看起来像template<class T> T& MyVector<T>::operator[] (std::ptrdiff_t index) { //Since vec2 points to the second element, we add 1. //For vec, we would add 0 since it points at the //first element in the array. std::size_t actual_index = static_cast<std::size_t>(index + 1); //Do boundary checking return this->ptr[actual_index]; }优点:我们现在能够访问数组中的所有元素。
缺点:用法变得笨拙和丑陋。 例如,如果我们创建一个大小为
std::numeric_limits<std::size_t>::max()的向量,那么为了访问最后一个元素,我们需要访问'-1' 元素:MyVector<int> big_vector(std::numeric_limits<std::size_t>::max()); big_vector[-1] = 5; // big_vector[-1] is the last element in the array. MyVector<int> big_vector2 = big_vector + 1; std::cout << big_vector2[-2] << std::endl; // would print 5, since big_vector2 points at the second element in the array
【问题讨论】:
-
如果您仍然有 64 位大小,那么只需使用解决方案 #1。您甚至不能希望拥有 9 EB 的可操作内存,对吗?
-
这只会是字符类型的问题,并且在典型的体系结构中整数换行会“自动”解决问题 - 如果您注意表达式。无论如何,正确验证
C++代码是否存在潜在的整数溢出是一个更大的问题,并且需要大量的关注和测试,因此在投入过多精力之前,请考虑它是否真的是您的应用程序的要求。 -
你可能有两个重载...
-
@TheParamagneticCroissant 是的,但我认为标准没有提到 std::ptrdiff_t 的下限。所以理论上,可能有一台机器 std::size_t 是 64 位长,但 std::ptrdiff_t 只有 16 位,这对于数组大小来说太低了。
-
@Mikrosaft:虽然
ptrdiff_t可能小于size_t,但这种架构非常罕见。编写完全便携的C/C++与所有可能的奇数球系统兼容几乎是不可能的(例如,由于非常规平台的稀有性,需要大量的小心、笨拙的变通方法和几乎不可能的测试)。编写C++代码的典型方法是选择你的战斗并选择要支持的实现选择的一个子集,然后如果你的猜测被证明是错误的,那么稍后重新测试/审查。大多数代码不需要在 24 位 DSP 上运行。
标签: c++ arrays indexing size-t ptrdiff-t