在使用STL容器时,我应该使用int还是unsigned int?

时间:2013-06-19 03:48:27

标签: c++ stl casting integer warnings

参考本指南:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Integer_Types#Integer_Types

Google建议大部分时间都使用int 我尝试遵循本指南,唯一的问题是STL容器。

例如:
1.

void SetElement(int Index, int Value)
{
    if (Index > Vector.size()) return;
    ...
}

如果int使用Index,我会收到警告。

2

for (int i = 0; i < Vector.size(); ++i)
{
    ...
}

说到循环计数器,同样的警告。

如果我将Indexi声明为unsigned int然后传播,我必须将更多变量声明为unsigned int,并且不会有一致性。

我能想到的最佳方式是使用如下的演员:
if (Index > static_cast<int>(Vector.size()) ...
for (int i = 0; i < static_cast<int>(Vector.size()); ++i) ...
但我真的不喜欢这些演员。

有任何建议的方法吗?

P.S。
链接给出的循环示例有更多的原因 要仅使用有符号整数,我可以避免签名/未签名警告,铸件,
并确保每个价值都可以是负数(为了保持一致),
并且我总是可以使用-1作为无效值。

在很多情况下,它会将循环计数器与某些常量或结构成员混合等。
如果签名/未签名不一致,那么总会有警告和铸件,
这很烦人,毫无意义 所以这就是为什么我要统一所有要签名的整数。

2 个答案:

答案 0 :(得分:11)

无符号类型有三个特征,其中一个是定性'好',其中一个定性'坏':

  • 它们可以容纳两倍于相同大小的签名类型(好)
  • 的值
  • size_t版本(即32位计算机上的32位,64位计算机上的64位等)可用于表示内存(地址,大小等)(中性) )
  • 它们包含在0以下,因此在循环中减去1或使用-1表示无效索引可能会导致错误(错误。)Signed types wrap

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

当值变为负数时,您应该使用带签名的类型。

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

处理内存大小时应使用size_t sizeof的结果,通常是字符串长度等)。通常选择默认的无符号类型使用,因为它匹配编译代码的平台。例如,字符串的长度为size_t,因为字符串只能包含0个或更多元素,并且没有理由将字符串的长度方法任意限制为比平台上可以表示的更短,例如32位平台上的16位长度(0-65535)。注意(感谢评论者Morwenstd::intptr_tstd::uintptr_t在概念上相似 - 将始终适合您的平台 - 并且如果您想要某些不是指针。注2(感谢评论者rubenvb)由于size_t-1的值,字符串只能容纳npos个元素。详情如下。

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

迭代

但是,使用基于整数的循环迭代数据结构的内容是在C ++中执行它的错误方法,所以从某种意义上说,有符号vs无符号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?'是'都不是。改为使用迭代器。'了解更多关于:

还剩下什么?

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

我非常相信使用自然类型,例如枚举,以及适合此的有符号整数。它们更符合我们的概念期望。当你的思想和代码对齐时,你不太可能编写错误的代码,更有可能表达出正确,干净的代码。

答案 1 :(得分:3)

使用容器返回的类型。在这种情况下,size_t - 这是一个无符号的整数类型。 (技术上,它是 std::vector<MyType>::size_type ,但通常定义为size_t,所以使用size_t是安全的。 unsigned 也很好)

但总的来说,使用合适的工具来做正确的工作。这个'指数'应该是负面的吗?如果没有,请不要签名。

通过,你不必输入'unsigned int'。 'unsigned'是相同变量类型的简写:

int myVar1;
unsigned myVar2;

原始问题中链接的页面说:

  

有些人,包括一些教科书作者,建议使用unsigned   表示从不否定的数字的类型。这是有意的   作为一种自我记录的形式。但是,在C中,这样的优点   文档被它可以引入的真正错误所抵消。

这不仅仅是自我记录,而是使用正确的工具来完成正确的工作。说'无符号变量可能导致错误所以不使用无符号变量'是愚蠢的。签名变量也可能导致错误。所以可以浮动(超过整数)。唯一保证无错误的代码是不存在的代码。

他们为什么无符号是邪恶的例子就是这个循环:

for (unsigned int i = foo.Length()-1; i >= 0; --i)

我很难在循环中向后迭代,而且我通常会犯错(带有符号或无符号整数)。我从大小中减去一个吗?我是否使它大于和等于0,或者只是大于?这是一个草率的情况开始。

那么你如何处理你知道自己遇到问题的代码?您可以更改编码样式以解决问题,使其更简单,并使其更易于阅读,并使其更易于记忆。他们发布的循环中有一个错误。错误是,他们希望允许值低于零,但他们选择使其无符号。这是他们的错误。

但这是一个简单的技巧,使阅读,记忆,编写和运行更容易。使用无符号变量。这是智能要做的事情(显然,这是我的意见)。

for(unsigned i = myContainer.size(); i--> 0; )
{
    std::cout << myContainer[i] << std::endl;
}

它没有签名。它总是有效的。起始大小没有负面影响。不用担心下溢。它只是有效。这很聪明。做得对,不要停止使用无符号变量,因为有人曾经说过他们在使用for()循环时遇到了错误,并且没有训练自己不犯错误

记住它的诀窍:

  1. 将'i'设置为大小。 (不要担心减去一个)
  2. 让'i'像箭头一样指向 0。 i --> 0(它是后递减(i - )和大于比较(i> 0)的组合)
  3. 最好自己动手编写代码,然后丢弃工具,因为你没有正确编码。

    您希望在代码中看到哪个?

    for(unsigned i = myContainer.size()-1; i >= 0; --i)
    

    或者:

    for(unsigned i = myContainer.size(); i--> 0; )
    

    不是因为输入的字符少(这很愚蠢),而是因为它不那么精神混乱。在浏览代码时进行心理解析更容易,更容易发现错误。


    <强> Try the code yourself