比较一个无符号整数与std::string::size_type安全吗?

4

我正在阅读Andrew Koenig和Barbara E.Moo的《Accelerated C++》一书,对第2章中的主要示例有些疑问。 以下是代码摘要,并且使用g ++编译时没有警告/错误:

#include <string>
using std::string;

int main()
{
    const string greeting = "Hello, world!";
    // OK
    const int pad = 1;
    // KO
    // int pad = 1;
    // OK
    // unsigned int pad = 1;
    const string::size_type cols = greeting.size() + 2 + pad * 2;
    string::size_type c = 0;
    if (c == 1 + pad)
    {;}

    return 0;
}

但是,如果我将const int pad = 1;替换为int pad = 1;,g ++编译器会返回一个警告:

warning: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
    if (c == 1 + pad)

如果我用unsigned int pad = 1;替换const int pad = 1;,g++编译器将不会返回警告。
我理解为什么g++会返回警告,但我对以下三点不确定:
- 使用unsigned intstd::string::size_type进行比较是否安全?编译器在这种情况下不会返回警告,但我不确定是否安全。 - 为什么编译器在原始代码const int pad = 1中没有发出警告。编译器是否自动将变量pad转换为unsigned int? - 我还可以用string::size_type pad = 1;替换const int pad = 1;,但是我认为变量pad的含义与字符串大小没有真正联系。即使如此,在这种情况下,这是否是避免比较中有不同类型的最佳方法?

从msdn:basic_string::size_type => typedef typename allocator_type::size_type size_type; => 一种无符号整数类型,可表示字符串中的元素和索引数量。(https://msdn.microsoft.com/library/5ddehwe8.aspx)。因此,使用无符号整数可能是一个不错的做法。 - Fefux
@Fefux 我不会相信微软在C/C++标准方面的权威来源。多年来,他们已经有足够多的“创造性”解释标准的经历了... - skyking
if ( (int)c == pad + 1 ) - i486
4个回答

2

从编译器的角度来看:

  1. 比较有符号和无符号变量(非常数)是不安全的。
  2. 比较两个不同大小的无符号变量是安全的。
  3. 如果编译器可以检查有符号变量类型的允许范围(例如,对于16位有符号整数,使用范围在[0..32767]的常量是安全的),则将无符号变量与有符号常量进行比较是安全的。

所以,您的问题的答案是:

  1. 是的,比较 unsigned intstd::string::size_type 是安全的。
  2. 没有警告,因为编译器可以在编译时执行安全检查。
  3. 在比较中使用不同的 unsigned 类型没有问题。请使用 unsinged int

1
如果您使用默认分配器(很可能是)的std :: string,则size_type实际上是size_t。
[support.types] / 6定义size_t为
一个实现定义的无符号整数类型,足够大以包含任何对象的大小(以字节为单位)。
因此,从技术上讲,并不能保证它是unsigned int,但我相信在大多数情况下都是这样定义的。
现在关于您的第二个问题:如果您使用const int something = 2,则编译器会看到此整数a)从不为负,并且b)永远不会更改,因此始终可以安全地将该变量与size_t进行比较。在某些情况下,编译器可能会完全优化掉该变量,并仅用2替换所有出现。
我认为最好在需要某些东西的大小时随处使用size_type,因为它更详细。

1
std::string::size_type不能保证与size_t相同。 - Mats Petersson
1
@MatsPetersson,没错,但如果您使用默认分配器,则为std::size_t,这似乎是OP的情况。 - SingerOfTheFall
不一定。有人可能会决定在64位机器上将长度存储为32位值具有某些特定的优势。不,这不是常见的做法,但仍然是可能的。 - Mats Petersson
@MatsPetersson,抱歉我不太明白。按照标准,std::stringstd::basic_string<char>,它是template< class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> > class basic_string;,并将size_type定义为其分配器的size_typestd::allocator::size_type又被定义为size_t,因此除非使用自定义分配器,否则应始终具有size_t。当然,在不同的实现中,size_t本身的大小可能不同,但我已在答案中提到了它。 - SingerOfTheFall

1
比较有符号和无符号值是“危险的”,因为当有符号值为负数时,您可能无法得到预期的结果(它可能表现为非常大的无符号值,因此a > ba = -1b = 100时会返回true。 (使用const int的原因是编译器知道该值不会改变,因此可以说“好的,这个值始终为1,所以在这里运行良好”)

只要要比较的值适合于unsigned int(在典型的机器上,略大于40亿),就可以了。


1
编译器警告的是无符号整数和有符号整数类型的比较。这是因为有符号整数可以是负数,其含义是反直觉的。这是因为在比较之前,有符号整数被转换为无符号整数,这意味着负数将比正数更大。
“使用unsigned int与std::string :: size_type进行比较是否安全?在那种情况下,编译器不会返回警告,但我不确定是否安全。”
是的,它们都是无符号的,然后语义是预期的。如果它们的范围不同,则较窄的类型将被转换为更宽的类型。
“为什么编译器不会在原始代码const int pad = 1中发出警告?编译器是否自动将变量pad转换为unsigned int?”
这是因为编译器的构造方式。编译器在发出警告之前解析并在某种程度上优化代码。重要的一点是,在考虑此警告时,编译器现在已知有符号整数为1,因此可以安全地与无符号整数进行比较。
我也可以将const int pad = 1;替换为string::size_type pad = 1;,但是在我看来,变量pad的意义并不真正与字符串大小相关。尽管如此,在这种情况下,避免比较时出现不同类型最好的方法是否采用这种方法? 如果您不希望它是常量,最好的解决方案可能是将其至少设置为无符号整数类型。但是,您应该知道普通整数类型和大小之间没有保证的关系,例如unsigned int可能比size_t和size_type更窄、更宽或相等(后者也可能不同)。

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