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

Why size of QByteArray is `int` rather than `unsigned int`

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

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?我怎样才能安全地摆脱这个警告?

一些人认为,多年前将 unsigned 类型引入 C 是一个坏主意。这些类型发现自己被引入到 C++ 中,在那里它们深深地嵌入到 C++ 标准库和运算符 return 类型中。

是的,sizeof 按照标准,return 必须是 unsigned 类型。

Qt 开发人员采用了现代思想,即 unsigned 类型是一个坏主意,并倾向于将 size 的 return 类型改为 signed 类型。我个人觉得它很特别。

要解决这个问题,您可以 (i) 接受警告,(ii) 在函数运行期间将其关闭,或者 (iii) 编写类似

的内容
(std::size_t)idx0.size() >= sizeof(ushortIdx0)

以牺牲清晰度为代价。

Why size() of QByteArray is returned as int rather than unsigned int?

我真的不知道为什么 Qt 为 size() 选择了带符号的 return。但是,有充分的理由使用有符号而不是无符号。

一个臭名昭著的 unsigned size() 失败的例子是这个看起来很无辜的循环:

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

使循环体对两个元素进行操作并不少见,在这种情况下,仅迭代到 some_container.size() - 1.

似乎是一个有效的选择

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

请注意,此问题很容易修复,但如果 size() 确实 return 有符号值,那么首先就没有问题需要修复。

因为在 Qt 容器中(如:QByteArray、QVector,...)有些函数可以 return 一个负数,如:indexOf、lastIndexOf、contains,...有些函数可以接受负数数字,例如:mid,...;因此,为了 class 兼容甚至框架兼容,开发人员使用签名类型 (int)。

您可以使用标准的 C++ 转换:

if ( static_cast<size_t>(idx0.size()) >= sizeof(ushortIdx0) )

原因是问题的重复部分,但类型不匹配的解决方案是一个有效的问题。对于您正在做的那种比较,将它们分解出来可能会有用,因为它们具有一定的可重用含义:

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 并将其专门用于比较而不是其他。