【发布时间】:2019-02-20 12:20:36
【问题描述】:
我有一些或多或少像这样的代码:
#include <bitset>
enum Flags { A = 1, B = 2, C = 3, D = 5,
E = 8, F = 13, G = 21, H,
I, J, K, L, M, N, O };
void apply_known_mask(std::bitset<64> &bits) {
const Flags important_bits[] = { B, D, E, H, K, M, L, O };
std::remove_reference<decltype(bits)>::type mask{};
for (const auto& bit : important_bits) {
mask.set(bit);
}
bits &= mask;
}
Clang >= 3.6 做了一件聪明的事并将其编译为单个 and 指令(然后在其他任何地方内联):
apply_known_mask(std::bitset<64ul>&): # @apply_known_mask(std::bitset<64ul>&)
and qword ptr [rdi], 775946532
ret
但是every version of GCC I've tried 将其编译成一个巨大的混乱,其中包括应该静态 DCE 的错误处理。在其他代码中,它甚至会将important_bits 等价于与代码一致的数据!
.LC0:
.string "bitset::set"
.LC1:
.string "%s: __position (which is %zu) >= _Nb (which is %zu)"
apply_known_mask(std::bitset<64ul>&):
sub rsp, 40
xor esi, esi
mov ecx, 2
movabs rax, 21474836482
mov QWORD PTR [rsp], rax
mov r8d, 1
movabs rax, 94489280520
mov QWORD PTR [rsp+8], rax
movabs rax, 115964117017
mov QWORD PTR [rsp+16], rax
movabs rax, 124554051610
mov QWORD PTR [rsp+24], rax
mov rax, rsp
jmp .L2
.L3:
mov edx, DWORD PTR [rax]
mov rcx, rdx
cmp edx, 63
ja .L7
.L2:
mov rdx, r8
add rax, 4
sal rdx, cl
lea rcx, [rsp+32]
or rsi, rdx
cmp rax, rcx
jne .L3
and QWORD PTR [rdi], rsi
add rsp, 40
ret
.L7:
mov ecx, 64
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call std::__throw_out_of_range_fmt(char const*, ...)
我应该如何编写这段代码,以便两个编译器都能做正确的事情?如果做不到这一点,我应该如何编写它才能保持清晰、快速和可维护?
【问题讨论】:
-
不使用循环,就不能用
B | D | E | ... | O构造掩码吗? -
枚举有位位置而不是已经扩展的位,所以我可以做
(1ULL << B) | ... | (1ULL << O) -
缺点是实际名称很长且不规则,并且很难看到掩码中有哪些标志以及所有线条噪音。
-
@AlexReinking 你可以成为一个
(1ULL << Constant)|每行,并在不同的行上对齐常量名称,这样会更容易。 -
我认为这里的问题与缺乏使用无符号类型有关,GCC 总是在有符号/无符号混合中静态丢弃溢出和类型转换的校正时遇到问题。这里的位移结果是
int结果位操作的可能是int或可能是long long,具体取决于值,并且正式的enum不等同于int常量。 clang 呼吁“好像”,gcc 保持迂腐
标签: c++ c++11 bit-manipulation