c++中如何使用std::popcount统计二进制位数_c++20位运算【详解】

11次阅读

std::popcountc++20 引入的用于计算无符号整数二进制中 1 的个数的 constexpr 函数,仅接受无符号类型,需对有符号数用 static_cast 保持位模式不变。

c++中如何使用std::popcount统计二进制位数_c++20位运算【详解】

std::popcount 是什么,为什么不能直接用

std::popcount 是 C++20 引入的 头文件中的函数,用于计算无符号整数二进制表示中 1 的个数(即汉明重量)。但它**仅接受无符号类型**,传入 intlong 等有符号类型会编译失败——这不是 bug,是标准明确要求的重载限制。

  • 合法调用:std::popcount(static_cast(x))std::popcount(uint64_t{val})
  • 非法调用:std::popcount(-1)std::popcount(42)(若 42 是 int 类型)
  • 底层依赖硬件指令(如 x86 的 popcnt),比手写循环或查表更快更可靠

如何安全传入有符号整数

多数场景下变量是 intlong,需显式转换。关键不是“转成 unsigned”,而是**保持位模式不变**——对补码系统,这等价于 reinterpret cast,但标准推荐用 static_cast 配合无符号对应类型。

  • int:用 static_cast(不推荐 unsigned,因大小不保)
  • long long:必须用 static_cast,否则可能截断
  • 对负数:std::popcount(static_cast(-1)) == 32(在 32 位 int 平台),这是预期行为
int x = -5; // 二进制补码:...11111011 auto count = std::popcount(static_cast(x)); // 正确:统计所有位中的 1

编译器支持与替代方案

Clang 11+、GCC 9.1+、MSVC 19.28+ 支持 std::popcount;但若项目需兼容 C++17 或更早,不能直接降级使用。此时不要手写朴素循环(O(N)),优先考虑:

  • __builtin_popcount(GCC/Clang):对 unsigned int__builtin_popcountllunsigned long long
  • _mm_popcnt_u32(MSVC + ),需启用 /arch:AVX2 或类似选项
  • 避免用 std::bitset::count() 做运行时值统计——它只接受编译期常量 N,且构造开销大

常见误用和性能陷阱

最典型的错误是忽略类型匹配,导致编译失败或静默调用错误重载(如误触发模板版本而没定义)。另一个容易被忽略的点是:std::popcount 不处理浮点数或指针,也不能用于数组或自定义类型。

立即学习C++免费学习笔记(深入)”;

  • 错误示例:std::popcount(0x123) —— 字面量类型是 int,非 unsigned,GCC 报错 no matching function
  • 正确写法:std::popcount(0x123u)std::popcount(0x123ULL)
  • 对齐无关:该函数不关心内存布局,只作用于值本身,无需担心结构体字段偏移
  • constexpr 友好:只要输入是字面量或 constexpr 变量,结果也是 constexpr
constexpr auto c = std::popcount(0b1010u); // c == 2,编译期求值

C++20 的 std::popcount 看似简单,但类型系统卡得严,实际用时几乎每次都要加 static_cast。别图省事写宏封装,容易掩盖有符号扩展问题;老老实实按类型配对,才是稳定之道。

text=ZqhQzanResources