【问题标题】:Why size of QByteArray is `int` rather than `unsigned int`为什么 QByteArray 的大小是`int`而不是`unsigned int`
【发布时间】:2019-02-08 11:30:55
【问题描述】:

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

QByteArray idx0 = ...

unsigned short ushortIdx0;

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

但我收到了警告:

警告:有符号和无符号整数表达式之间的比较 [-Wsign-compare]

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

为什么QByteArray 中的size() 返回为int 而不是unsigned int?如何安全地消除此警告?

【问题讨论】:

标签: c++ qt qbytearray


【解决方案1】:

有些人认为多年前将unsigned 类型引入C 是一个坏主意。这些类型被引入到 C++ 中,并深深嵌入到 C++ 标准库和运算符返回类型中。

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

Qt 开发人员采用现代思维认为unsigned 类型是一个坏主意,并倾向于将size 的返回类型改为signed 类型。我个人觉得它很特殊。

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

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

以清晰度为代价。

【讨论】:

  • “Qt 开发人员采用现代思维” 你从哪里得到这个?几年前我提出了一个问题,将 Qt 切换到size_t,但被拒绝了,但只是基于它会涉及太多变化,没有意识形态上的反对。
  • @ixSci:有讨论过吗?能给个链接吗?
  • @geza,我会在几个小时内尝试找到它。那是在 Qt5 发布之前,从那时起,Qt 相关资源有很多来回移动,所以没有任何承诺。
  • "(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),将无符号与有符号混合的乐趣:-/
  • 他们可以为 "invalid" 对象返回 "conventional" -1
【解决方案2】:

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

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

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() 确实返回有符号值,则无需首先解决问题。

【讨论】:

  • 是的,我前段时间回答了一个问题。显而易见的解决方案是i + 1 &lt; some_container.size()。不是这个问题的真正答案,但无论如何都赞成,因为它很重要!
  • @Batsheba 是的,它当然很容易修复,但是首先要修复的问题并不好。此外,并非所有此类错误的发生都那么容易发现
  • 幻灯片操作符就是为此而发明的!
  • @Bathsheba 是什么运算符?
【解决方案3】:

因为在 Qt 容器中(例如:QByteArray、QVector、...)中存在可以返回负数的函数,例如:indexOf、lastIndexOf、contains、...有些可以接受负数,例如:mid、 ...;因此,为了类兼容甚至框架兼容,开发人员使用有符号类型 (int)。

您可以使用标准 c++ 转换:

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

【讨论】:

    【解决方案4】:

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

    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) &gt; 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 并将其专门用于比较,仅此而已。

    【讨论】:

      猜你喜欢
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-21
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多