为什么 GCC 错误地检测到移位计数溢出?
Why is GCC wrongly detecting shift count overflow?
我正在尝试从常量字符串中创建最多 8 个字符的编译时标签集。比如“abc”应该变成0x6162630000000000,“”应该变成0等等
为此,我想我应该利用对数组的引用不会衰减到指针这一事实,我可以用这个捕获 const 数组的 N(大小):
template <size_t N>
inline constexpr uint64_t make_tag(char const (&arr)[N]) {
if (N == 1) {
return 0;
}
uint64_t result = 0;
for (size_t i = 0; (i < (N-1)) && (i<8); i++) {
result = (result << 8) | arr[i];
}
if ((N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
return result;
}
N 被正确捕获,“”为 1,其余字符串为 len+1。
这是让我感到困惑的事情:当做类似
的事情时
static constexpr uint64_t a0=make_tag("");
GCC 11.2 告诉我这个:
<source>: In instantiation of 'constexpr uint64_t make_tag(const char (&)[N]) [with long unsigned int N = 1; uint64_t = long unsigned int]':
<source>:24:43: required from here
<source>:17:33: error: left shift count >= width of type [-Werror=shift-count-overflow]
17 | result = result << (8 * (8 - (N-1)));
| ~~~~~~~^~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
clang 不会那样做。
为方便起见,这里是 compiler explorer snippet:
编辑:if (N == 1) {...} else {...}
按照@Useless 的建议做了技巧
Why is GCC wrongly detecting shift count overflow?
不是。
当N=1
时,代码
if ((N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
产生 64 位移位。
如果您不想为 N=1
编译该代码,请明确说明:
if (N > 1 && (N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
将所有内容都放在初始检查 else
分支中 if (N ==1) { return 0; } else { ...
也可以。
目前还不清楚为什么 GCC 没有传播早期 return 隐含的 N
约束,但我怀疑它根本不需要。
注意 if constexpr
周围的标准语言说:
If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement.
if constexpr
语句之后静态丢弃任何内容,即使采用的分支有一个早期的 return。当然可以优化掉,但首先还是要能编译。
我正在尝试从常量字符串中创建最多 8 个字符的编译时标签集。比如“abc”应该变成0x6162630000000000,“”应该变成0等等
为此,我想我应该利用对数组的引用不会衰减到指针这一事实,我可以用这个捕获 const 数组的 N(大小):
template <size_t N>
inline constexpr uint64_t make_tag(char const (&arr)[N]) {
if (N == 1) {
return 0;
}
uint64_t result = 0;
for (size_t i = 0; (i < (N-1)) && (i<8); i++) {
result = (result << 8) | arr[i];
}
if ((N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
return result;
}
N 被正确捕获,“”为 1,其余字符串为 len+1。
这是让我感到困惑的事情:当做类似
的事情时static constexpr uint64_t a0=make_tag("");
GCC 11.2 告诉我这个:
<source>: In instantiation of 'constexpr uint64_t make_tag(const char (&)[N]) [with long unsigned int N = 1; uint64_t = long unsigned int]':
<source>:24:43: required from here
<source>:17:33: error: left shift count >= width of type [-Werror=shift-count-overflow]
17 | result = result << (8 * (8 - (N-1)));
| ~~~~~~~^~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
clang 不会那样做。
为方便起见,这里是 compiler explorer snippet:
编辑:if (N == 1) {...} else {...}
按照@Useless 的建议做了技巧
Why is GCC wrongly detecting shift count overflow?
不是。
当N=1
时,代码
if ((N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
产生 64 位移位。
如果您不想为 N=1
编译该代码,请明确说明:
if (N > 1 && (N-1) < 8) {
result = result << (8 * (8 - (N-1)));
}
将所有内容都放在初始检查 else
分支中 if (N ==1) { return 0; } else { ...
也可以。
目前还不清楚为什么 GCC 没有传播早期 return 隐含的 N
约束,但我怀疑它根本不需要。
注意 if constexpr
周围的标准语言说:
If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement.
if constexpr
语句之后静态丢弃任何内容,即使采用的分支有一个早期的 return。当然可以优化掉,但首先还是要能编译。