为什么C++数组索引值是有符号的,而不是围绕size_t类型构建的(或者我错了)



对我来说,跟踪不断发展的C++标准变得越来越困难,但现在对我来说似乎很清楚的一件事是数组索引值应该是整数(不是long longsize_t或其他一些看似更合适的大小选择)。我从这个问题的答案(C++数组索引的类型)以及成熟的C++库(如 Qt)使用的实践中推测了这一点,这些库也使用简单的整数作为大小和数组索引运算符。 对我来说,棺材上的钉子是我现在收到来自MSVC 2017的大量编译器警告,指出我的const unsigned long long(又名const size_t)变量在用作数组索引时被隐式转换为类型const int

Mat在上面链接的问题中给出的答案引用了ISO C++标准草案n3290的话

它应是一个整数常数表达式,其值应大于零。

我没有阅读这些规范和精确解释其语言的背景,所以也许有几点澄清:

  • "整型常量表达式">是否特别禁止像long long这样对我来说是整型的东西,只是一个更大的尺寸?
  • 他们所说的是否特别禁止像size_t这样标记为unsigned的类型?

如果我在这里看到的都是真的,那么数组索引值应该是signed int类型,为什么?这对我来说似乎是违反直觉的。规范甚至指出表达式"应大于零">,因此如果它是signed,我们会浪费一点。当然,我们可能仍然希望以某种方式将索引与0进行比较,这对于unsigned类型来说是危险的,但是应该有更便宜的方法来解决这个问题,只浪费一个值,而不是整个位。

此外,随着寄存器的不断扩大,一个更面向未来的解决方案是允许索引的更大类型(如long long),而不是坚持使用int这在历史上无论如何都是有问题的类型(当处理器更改为32 bits时更改其大小,然后当它们转到64 bits时不更改)。 我什至看到有些人谈论size_t轶事,就像它被设计成一种更面向未来的类型,用于尺寸(而不仅仅是为sizeof操作员服务返回的类型)。但当然,这可能是杜撰的。

我只是想确保我在这里的基础编程理解没有缺陷。当我看到像ISO C++小组这样的专家在做某事,或者Qt的工程师时,我会怀疑他们有充分的理由!对于像数组索引这样对编程如此基础的东西,我觉得我需要知道这个原因是什么,否则我可能会错过一些重要的东西。

查看 [expr.sub]/1 我们有

后缀表达式后

跟方括号中的表达式是后缀表达式。其中一个表达式应为"T 数组"类型的 glvalue 或类型为"指向 T 的指针"的 prvalue,另一个表达式应为无作用域枚举或整型的 prvalue。结果的类型为"T"。67 表达式 E1[E2] (根据定义)与 *((E1)+(E2))相同),只是在数组操作数的情况下,如果该操作数是左值,则结果为左值,否则为 x值。表达式 E1 在表达式 E2 之前排序。

强调我的

因此,下标运算符的索引必须是无作用域枚举或整型。 在[basic.fundamental]中,我们看到标准整数类型是signed charshort intintlong intlong long int,以及它们的无符号对应物。

因此,任何标准整数类型都可以使用,任何其他整数类型(如size_t)都是用作数组索引的有效类型。 提供给下标运算符的值甚至可以具有负值,只要该值可以访问有效元素即可。

我认为标准库 API 更喜欢索引是无符号类型。如果您查看文档以了解std::size_t它指出

在索引C++容器(如std::stringstd::vector等)时,适当的类型是此类容器提供的成员 typedefsize_type。它通常被定义为std::size_t的同义词。

在查看诸如std::vector::at等功能的签名时,这一点得到了加强

reference       at( size_type pos );
const_reference at( size_type pos ) const;

我认为您混淆了两种类型:

  1. 第一种类型是可用于定义数组大小的对象/值的类型。不幸的是,您链接到的问题使用索引,他们应该使用数组大小。这必须是必须在编译时计算的表达式,并且其值必须大于零。

    int array[SomeExpression]; // Valid as long as SomeExpression can be evaluated 
    // at compile time and the value is greater than zero.
    
  2. 第二种类型是可用于访问数组的对象/值的类型。鉴于上述array

    array[i] = SomeValue; // i is an index to access the array
    

    i不需要在编译时计算,i必须在 [0, SomeExpression-1] 范围内。但是,可以使用负值作为索引来访问数组。由于array[i]被计算为*(array+i)(暂时忽略重载的operator[]函数),如果array恰好指向数组的中间,i可以是负值。我对另一篇SO帖子的回答提供了有关该主题的更多信息。

    顺便说一句,由于array[i]被评估为*(array+i),因此使用i[array]是合法的,并且与array[i]相同。

相关内容

最新更新