为什么 QByteArray 的大小是 `int` 而不是 `unsigned int`?

4

我在代码中有这样的表达式:

QByteArray idx0 = ...

unsigned short ushortIdx0;

if ( idx0.size() >= sizeof(ushortIdx0) ) {
    // Do something
}

但是我收到了警告:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

if ( idx0.size() >= sizeof(ushortIdx0) ) {
              ~~~~~~~~~~~~^~~~~~~~~~

为什么QByteArraysize()返回值是int而不是unsigned int?我该如何安全地消除这个警告?


1
一个很好的文档回答: https://dev59.com/fl8d5IYBdhLWcg3wnzRz#26621565 - ymoreau
知道为什么是重复问题 - 这种知识并不影响您如何消除此警告。然而,如何修复比较是非常有效的问题! - Kuba hasn't forgotten Monica
4个回答

4

为什么QByteArray的size()返回类型是int而不是unsigned int?

我真的不知道为什么Qt选择了带符号返回值的size()。但是,使用带符号而不是无符号有很好的理由。

一个臭名昭著的例子是下面这个看起来很无辜的循环:

for (int i = 0; i < some_container.size() - 1; ++i) {
     do_somehting(some_container[i] , some_container[i+1] );
}

在循环体中操作两个元素并迭代到some_container.size() - 1似乎是一个有效的选择,这种情况下只需要迭代到容器大小减1。

然而,如果容器为空,则some_container.size()-1将默默地(无符号溢出定义良好)变成无符号类型的最大值。因此,与其避免越界访问,不如导致您可以获得的最大越界。

请注意,对于这个问题有简单的修复方法,但是如果size()返回一个有符号值,则首先不需要修复任何问题。


1
是的,我之前回答过这个问题。显而易见的解决方案是 i + 1 < some_container.size()。虽然不是问题的答案,但因为它很重要,所以被点赞了! - Bathsheba
@Batsheba,当然很容易修复,但首先存在需要修复的问题并不好。此类错误的所有出现并非都容易发现。 - 463035818_is_not_a_number
这就是幻灯片操作符被发明的原因! - Bathsheba
1
@Bathsheba 什么运算符? - 463035818_is_not_a_number

4
一些人认为C语言多年前引入了无符号类型是一个不好的想法,这种类型随后被引入到C++中,并深深地嵌入在C++标准库和运算符返回类型中。
是的,按照标准,sizeof必须返回一个无符号类型。
Qt开发者采用现代化思想,认为无符号类型是一个不好的想法,相反,他们倾向于将size的返回类型设为有符号类型。个人认为这有点古怪。
为了解决问题,你可以选择(i)忽略警告,(ii)在函数执行期间关闭警告,或者(iii)编写类似以下代码:
(std::size_t)idx0.size() >= sizeof(ushortIdx0)

以清晰为代价。

2
@ixSci:有关此事进行了讨论吗?你能给我们提供一个链接吗? - geza
@geza,我会在几个小时内尝试找到它。这是在Qt5发布之前,自那以后Qt相关资源来回移动了很多次,所以不能保证。 - ixSci
1
"(std::size_t)idx0.size() >= sizeof(ushortIdx0)"。这假设idx0.size()是非负的,否则可能会失败OP的意图 :-) 另一方面,将sizeof(ushortIdx0)转换为signed对于非常巨大的ushortIdx0来说将是UB :-)。 idx0.size()>=0 && (std::size_t)idx0.size()>=sizeof(ushortIdx0),混合使用无符号和有符号的选择 :-/ - Jarod42
@Jarod42:如果某个东西的大小曾经为负数,那么我认为我们都可以与开发团队进行非常充分的交流。 - Bathsheba
1
他们可以为“无效”的对象返回传统的“-1”。 - Jarod42
显示剩余5条评论

3
因为在Qt容器(例如:QByteArray、QVector等)中有一些函数可以返回负数,比如indexOf、lastIndexOf、contains等,还有一些函数可以接受负数,比如mid等。因此为了与类兼容甚至是框架兼容,开发者使用带符号的类型(int)。
您可以使用标准的C++转换方式:
if ( static_cast<size_t>(idx0.size()) >= sizeof(ushortIdx0) )

1

为什么会有重复的问题是一个问题的重复部分,但类型不匹配的解决方案是一个需要解决的有效问题。对于你正在进行的比较,将它们因素化可能是有用的,因为它们具有某种可重复使用的含义:

template <typename T> bool fitsIn(const QByteArray &a) {
  return static_cast<int>(sizeof(T)) <= a.size();
}

template <typename T> bool fitsIn(T, const QByteArray &a) {
  return fitsIn<T>(a);
}

if (fitsIn(ushortIdx0, idx0)) ...

希望您只有几种这样的比较,最好DRY(不要重复自己),而不是复制粘贴类型转换,使用专门用于此任务的函数 - 这些函数还表达了原始比较的意图。然后,可以轻松集中处理任何您想处理的特殊情况,例如当sizeof(T) > INT_MAX时。
另一种方法是定义一个新类型来包装size_t并使其适应您需要与之一起使用的类型:
class size_of {
  size_t val;
  template <typename T> static typename std::enable_if<std::is_signed<T>::value, size_t>::type fromSigned(T sVal) {
    return (sVal > 0) ? static_cast<size_t>(sVal) : 0;
  }
public:
  template <typename T, typename U = std::enable_if<std::is_scalar<T>::value>::type> 
  size_of(const T&) : val(sizeof(T)) {}
  size_of(const QByteArray &a) : val(fromSigned(a.size())) {}
  ...
  bool operator>=(size_of o) const { return value >= o.value; }
};

if (size_of(idx0) >= size_of(ushortIdx0)) ...

这将在概念上扩展 sizeof 并将其专门用于比较,而不涉及其他任何内容。

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