c++ - 使用 STL 容器时应该使用 int 还是 unsigned int?

标签 c++ stl casting integer warnings

引用本指南:
https://google.github.io/styleguide/cppguide.html#Integer_Types
谷歌建议使用 int在大部分时间。
我尝试遵循本指南,唯一的问题是 STL 容器。

示例 1。

void setElement(int index, int value)
{
    if (index > someExternalVector.size()) return;
    ...
}
比较 index.size()正在生成警告。
示例 2。
for (int i = 0; i < someExternalVector.size(); ++i)
{
    ...
}
i 之间的相同警告和 .size() .

如果我声明 indexiunsigned int ,警告已关闭,但类型声明会传播,然后我必须将更多变量声明为 unsigned int ,则它与指南相矛盾并失去一致性。
我能想到的最好方法是使用类型转换:if (index > static_cast<int>(someExternalVector.size())或者for (int i = 0; i < static_cast<int>(someExternalVector.size()); ++i)但我真的不喜欢 Actor 阵容。
有什么建议吗?

下面是一些详细的想法:
仅使用有符号整数的好处是:我可以避免有符号/无符号警告、强制转换,并确保每个值都可以是负数(以保持一致),因此 -1 可用于表示无效值。
在很多情况下,循环计数器的使用与一些其他常量或结构成员混合使用。因此,如果签名/未签名不一致,则会出现问题。会有很多警告和铸件。

最佳答案

无符号类型具有三个特征,其中一个是定性的“好”,另一个是定性的“坏”:

  • 它们可以容纳两倍于相同大小的有符号类型(良好)的值
  • size_t版本(即 32 位机器上的 32 位,64 位机器上的 64 位等)对于表示内存(地址、大小等)(中性)很有用
  • 它们低于 0,因此在循环中减 1 或使用 -1 表示无效索引可能会导致错误(坏)。Signed types wrap也。

  • STL 使用无符号类型是因为上面的前两点:为了不限制类数组类的潜在大小,例如 vectordeque (尽管您必须质疑在数据结构中需要 4294967296 个元素的频率);因为负值永远不会成为大多数数据结构的有效索引;因为size_t是用于表示与内存有关的任何事情的正确类型,例如结构的大小,以及相关的事情,例如字符串的长度(见下文)。这不一定是将它用于索引或其他的好理由非内存用途,例如循环变量。在 C++ 中这样做的最佳实践的原因是一种反向构造,因为它是在容器和其他方法中使用的,一旦使用,其余代码必须匹配以避免遇到同样的问题。

    当值可能变为负数时,您应该使用有符号类型。

    当值不能变为负数时,您应该使用无符号类型 (可能与“不应该”不同。)

    您应该使用 size_t处理内存大小时 ( sizeof 的结果,通常是字符串长度等)它通常被选为默认的无符号类型来使用,因为它与代码编译的平​​台相匹配。例如,字符串的长度为 size_t因为一个字符串只能有 0 个或多个元素,并且没有理由限制字符串的长度方法任意短于平台上可以表示的长度,例如 16 位长度 (0-65535) 在 32-位平台。注意(感谢评论者 Morwen)std::intptr_tstd::uintptr_t它们在概念上是相似的——总是适合你的平台的大小——如果你想要不是指针的东西,应该用于内存地址。注意 2(感谢评论者 rubenvb)一个字符串只能容纳 size_t-1元素归因于 npos 的值.详情如下。

    这意味着如果使用 -1 表示无效值,则应使用有符号整数。如果您使用循环向后迭代您的数据,并且您不确定循环构造是否正确,则应考虑使用有符号整数(并且如其他答案之一所述,它们很容易出错。)IMO ,你应该不耍花招确保代码有效——如果代码需要技巧,那通常是一个危险信号。此外,关注您并阅读您的代码的人将更难以理解。这两个都是不遵循@Jasmin Gray 上面的回答的原因。

    迭代器

    但是,使用基于整数的循环来迭代数据结构的内容是在 C++ 中执行此操作的错误方法,因此从某种意义上说,关于有符号与无符号 for 循环的争论是没有实际意义的。您应该改用迭代器:
    std::vector<foo> bar;
    for (std::vector<foo>::const_iterator it = bar.begin(); it != bar.end(); ++it) {
      // Access using *it or it->, e.g.:
      const foo & a = *it;
    

    执行此操作时,您无需担心类型转换、签名等问题。

    迭代器可以向前(如上)或反向,用于向后迭代。使用与 it != bar.end() 相同的语法, 因为 end()表示迭代结束,而不是底层概念数组、树或其他结构的结束。

    换句话说,回答您的问题“在使用 STL 容器时我应该使用 int 还是 unsigned int?”是'都不是。改用迭代器。 阅读更多关于:
  • Why use iterators instead of array indices in C++?
  • Why again (some more interesting points in the answers to this question)
  • Iterators in general - the different kinds, how to use them, etc.

  • 还剩下什么?

    如果循环不使用整数类型,还剩下什么?您自己的值取决于您的数据,但在您的情况下包括使用 -1 表示无效值。这很简单。使用签名。只要保持一致。

    我非常相信使用自然类型,例如枚举,并且有符号整数适合于此。它们更符合我们的概念预期。当你的思想和代码保持一致时,你就不太可能写出有缺陷的代码,而更有可能写出正确、干净的代码。

    关于c++ - 使用 STL 容器时应该使用 int 还是 unsigned int?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17182571/

    相关文章:

    c++ - 一种使用不同风格的私有(private)成员的方法

    c++ - 使用openssl BIO逐 block 解码base64

    iphone - Objective-C 中 C++ STL 容器 "pair<T1, T2>"的等价物?

    objective-c - 关于从整数转换指针而不进行强制转换的警告-需要说明

    ios - 从 AnyObject 转换为 CGColor?没有错误或警告

    c++ - 使用递归解决河内难题

    c++ - RapidXML 访问兄弟节点似乎无缘无故地导致段错误

    c++ - 为什么我的 std::wofstream 写的是 ansi?

    c++ - 转换和积累

    ios - 为什么从 UITextField 输入的字符串中四舍五入到较低的整数?