如何处理通用代码中可能混合的有符号和无符号算术?

Oli*_*ock 5 c++ unsigned signed

混合有符号和无符号算术的典型示例似乎是:

\n
    unsigned int u = 10;\n    int          a = -42;\n    auto tmp1 = u - a;\n    std::cout << tmp1 << std::endl;\n    int tmp2 = tmp1;\n    std::cout << tmp2 << std::endl;\n
Run Code Online (Sandbox Code Playgroud)\n

(请注意,我使用的是auto tmp1这样我可以显示中间结果并检查类型。当然,在实际代码中,这都是一行)

\n

如果你启用足够多的警告(-Wconversion在 clang-13 上,另外,-Wsign-conversion在 gcc-11 上),编译器会很好地抱怨:

\n
main.cpp:148:21: warning: conversion to \xe2\x80\x98unsigned int\xe2\x80\x99 from \xe2\x80\x98int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  148 |     auto tmp1 = u - a;\n      |                     ^\nmain.cpp:150:16: warning: conversion to \xe2\x80\x98int\xe2\x80\x99 from \xe2\x80\x98unsigned int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  150 |     int tmp2 = tmp1;\n
Run Code Online (Sandbox Code Playgroud)\n

但至少“答案是正确的”:

\n

output

\n
52\n52\n
Run Code Online (Sandbox Code Playgroud)\n

很好地解释了为什么这仍然“有效”,在这里:\n /sf/answers/1792679801/

\n

当然,一般且正确的建议是:“不要混合有符号和无符号类型”。

\n

然而,最近,在用户选择类型的通用代码中,我遇到了这个简化的示例:

\n
main.cpp:148:21: warning: conversion to \xe2\x80\x98unsigned int\xe2\x80\x99 from \xe2\x80\x98int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  148 |     auto tmp1 = u - a;\n      |                     ^\nmain.cpp:150:16: warning: conversion to \xe2\x80\x98int\xe2\x80\x99 from \xe2\x80\x98unsigned int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  150 |     int tmp2 = tmp1;\n
Run Code Online (Sandbox Code Playgroud)\n

如果您启用了正确的警告,编译器仍然会很好地抱怨:

\n
main.cpp:155:17: warning: conversion to \xe2\x80\x98unsigned int\xe2\x80\x99 from \xe2\x80\x98int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  155 |     auto avg1 = sum  / count;\n      |                 ^~~\nmain.cpp:157:16: warning: conversion to \xe2\x80\x98int\xe2\x80\x99 from \xe2\x80\x98unsigned int\xe2\x80\x99 may change the sign of the result [-Wsign-conversion]\n  157 |     int avg2 = avg1;\n
Run Code Online (Sandbox Code Playgroud)\n

答案是灾难性的错误

\n

output

\n
429496719\n429496719\n
Run Code Online (Sandbox Code Playgroud)\n

在实际代码中,它在一个循环中运行,并avg反馈到sum. 于是sum很快就溢出来了。有符号整数溢出这是未定义的行为。我的UBSan尽职尽责地开始了,蝴蝶从电脑里飞出来给我做午饭。

\n

当用户选择类型时,在通用代码中解决此问题的安全方法是什么?

\n
    \n
  1. 显而易见的auto avg1 = sum / static_cast<int>(count); ??有效,不再有 UB,正确答案,并且没有警告。但看起来像是一个木棍/大锤,并且可能隐藏其他问题?
  2. \n
  3. 限制用户选择的类型(使用概念sum或类似概念) ,使得、countavg始终具有相同的“符号”。
  4. \n
\n

或者完全是别的什么......?

\n

编辑

\n

为了清楚起见,下面是玩具模板的样子,以及用户可能会如何被抓住。

\n
52\n52\n
Run Code Online (Sandbox Code Playgroud)\n

Output

\n
-10\n429496719\n
Run Code Online (Sandbox Code Playgroud)\n

对于那些对较长的激励示例感兴趣的人,此代码审查中已详细说明了所有内容:\n https://codereview.stackexchange.com/questions/274171/generic-exponential-damping-to-smooth-noisy-signals

\n