【问题标题】:What is the least intrusive way to silence a signed/unsigned warning in C++?在 C++ 中使有符号/无符号警告静音的最不干扰的方法是什么?
【发布时间】:2019-06-03 17:30:03
【问题描述】:

我有一个套接字通信程序。该协议是任何写入错误都是致命的,因此应该关闭连接。我的 I/O 代码如下所示:

auto const toWrite = buf.size() * sizeof(buf[0]);
auto nWritten = ::write(fd, buf.data, toWrite);

if (toWrite != nWritten)
{
    closeTheSocket();
}

此代码在布尔测试中给出warning: comparison between signed and unsigned integer expressions

我了解有符号与无符号比较的大/小比较的弊端,但在这里这是不可避免的。 ::write 系统调用的签名是

  #include <unistd.h>

  ssize_t write(int fd, const void *buf, size_t count);

换句话说,我的toWrite 变量是正确无符号的,而返回的nWritten 是有符号的(-1 表示错误)。我不在乎;除了完全传输之外的任何东西对连接都是致命的。另外,我不明白有符号/无符号之间的(不)相等测试是如何危险的。

我看过herehereherehere,但问题都是关于小于比较的问题,答案都是“不要那样做”。

This question 询问有关消除警告的问题,但大锤“消除所有签名/未签名”比较是不可取的。

我应该如何以尽可能不打扰的方式使仅此警告静音?

【问题讨论】:

  • static_cast&lt;ssize_t&gt;(toWrite)
  • “我应该如何以尽可能不打扰的方式使仅此警告静音?”在您确定的地方使用显式强制转换你在做。
  • 问题不仅仅是“强制转换”,因为他必须能够检测到::write 的显式失败条件,并且您必须确保强制转换不会丢失有用的信息,因此您有投射到较大的正范围内。
  • 我不明白您为什么不应该将 auto const toWrite = ... 更改为 ssize_t const toWrite = ...
  • @PaulSanders 因为系统调用::write() 需要一个无符号参数。

标签: c++ unsigned gcc-warning


【解决方案1】:

将错误条件的检测与长度不正确的检测分开,并使用显式强制转换

if ( nWritten < 0 ||
     static_cast<decltype(toWrite)>(nWritten) != toWrite )
{
   // handle problems
}

小修改:将所有负值捕获为错误,以备日后验证。

【讨论】:

  • 感谢您发布答案。是否需要对 -1 进行显式测试?它不会自然而然地退出!= 测试吗?我一直在尝试思考可能是真的病态,但我看到的唯一优势并不值得担心。
  • @jwm 即使是这样,你为什么要混淆代码?如果这两个条件可以合二为一,那就让编译器去做吧,它一定要知道
  • @jwm 将负符号值转换为无符号类型时会发生什么?但是以另一种方式进行转换也有问题,因为当您从无法在有符号范围中表示的无符号范围的一部分进行转换时会发生什么?这两个问题都有答案,但都是复杂的;这样一来,读者就可以准确地看到您在做什么,而不必回忆有关演员如何工作的晦涩细节。
  • @foreknownas_463035818 是否混淆?我认为原始代码所说的是“如果写入的字节数不是我所期望的,则失败”。这段代码所说的是“如果写入返回和错误条件,或者没有写入预期的字节数,则失败”。它添加了一个附加条件(我相信)包含在!=
  • @jwm 想象一下,你们两种类型的大小都是一个字节,而你写了 255_10 个字节。在 2 的补码机器上,255 的无符号表示和 -1 的有符号表示都是 11111111_2。然后呢?
【解决方案2】:

如果您可以裸露一些模板样板,另一种可能的解决方案是编写一个以不同方式处理每种类型的函数:

#include <type_traits>

template <class A, class B>
constexpr bool are_different(A a, B b)
{
    if constexpr (std::is_signed_v<A> and std::is_unsigned_v<B>)
    {
        if ( a < 0 )
            return true;
        else
            return std::make_unsigned_t<A>(a) != b;
    }
    else if constexpr (std::is_unsigned_v<A> and std::is_signed_v<B>)
    {
        if ( b < 0 )
            return true;
        else
            return a != std::make_unsigned_t<B>(b);
    }
    else
    {
        return a != b;
    }
}

int main()
{
    static_assert(are_different(1, 2));
    static_assert(!are_different(1ull, 1));
    static_assert(are_different(1, 2));
    static_assert(are_different(1u, 2));
    static_assert(are_different(1, 2u));
    static_assert(are_different(-1, -1u));
    static_assert(!are_different((long long)-1u, -1u));
}

【讨论】:

  • 这很有趣。我不知道std::make_(un)signed。我找不到很多关于您何时可能想要使用它的信息(我怀疑它在某些模板编写应用程序中具有实用性),但它与 static_cast 有何不同?
  • @jwm 参见例如en.cppreference.com/w/cpp/types/make_unsigned“提供了成员typedef类型,它是对应于T的无符号整数类型,具有相同的cv-qualifiers”,同样,用这个名字,你的意图就很清楚了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 2014-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多