std::has_single_bit 是 C++20 引入的 constexpr 函数,用于判断无符号整数是否恰好只有一个位为 1(即为 2 的非负整数次幂),返回 true 或 false,不指示具体位置,且对 0 或有符号数返回 false 或编译错误。

std::has_single_bit 是什么,它能检查什么
std::has_single_bit 是 C++20 引入的 constexpr 工具函数,用于判断一个整数是否恰好只有一位被置为 1(即该数是 2 的非负整数次幂:1, 2, 4, 8, …)。它不关心“位置”,只回答“是不是只有一个 bit 为 1”。常见误用是以为它能返回具体哪一位——它不能,它只返回 true 或 false。
它要求参数为无符号整数类型(unsigned int、std::size_t 等),传入有符号数或 0 会直接返回 false(注意:0 不满足“单一位”,所以结果正确)。
-
std::has_single_bit(8)→true(二进制1000) -
std::has_single_bit(7)→false(111,三位为 1) -
std::has_single_bit(0)→false(没有位被置 1) -
std::has_single_bit(-1)→ 编译错误(类型不匹配,需先转为无符号)
怎么配合位运算定位具体位置
如果不仅要判断“是否单一位”,还要知道“在哪一位”,std::has_single_bit 只负责前半部分;后半部分得靠 std::countr_zero(C++20)或手动位移。两者组合才是完整解法。
典型写法:
立即学习“C++免费学习笔记(深入)”;
if (std::has_single_bit(x)) {
int pos = std::countr_zero(x); // 返回末尾连续 0 的个数,即最低位 1 的索引(从 0 开始)
}- 输入
x = 16(10000),std::countr_zero(16)返回4 - 必须先确认
std::has_single_bit(x)为true,否则std::countr_zero(0)返回sizeof(T)*CHAR_BIT(未定义行为风险),而其他多比特数的结果无业务意义 - 不要用
log2(x)替代——浮点计算不精确、不可 constexpr、且对x=0或x=1易出错
为什么不用 x & (x - 1) == 0 代替
手写 (x != 0) && ((x & (x - 1)) == 0) 确实等价于 std::has_single_bit(x),但有实际差异:
- 可读性:后者需理解位运算技巧;前者语义直白,一读即知意图
- constexpr 支持:C++20 起
std::has_single_bit是标准 constexpr 函数,编译期可用;手写表达式虽通常也能 constexpr,但依赖编译器优化,某些嵌入式平台或旧标准下可能受限 - 类型安全:手写版本对有符号负数可能产生未定义行为(如
-1 & -2);std::has_single_bit只接受无符号类型,编译期就拦截错误 - 性能:现代编译器对二者生成的汇编几乎一致(常为一条
test+jz),无需担心效率损失
容易忽略的边界和移植问题
使用时最常踩的坑不是逻辑,而是类型和标准版本:
- 必须开启 C++20 或更高标准(
-std=c++20),否则编译失败 - 头文件只需
<bit>,但有些旧版 libstdc++(如 GCC 10)需额外定义_GLIBCXX_USE_C99_MATH_TR1,Clang 则基本无问题 - 参数必须是无符号整型;
std::has_single_bit(static_cast<unsigned>(x))是安全转换方式,避免符号扩展污染高位 - 对
std::size_t等宽类型,确保值未溢出——例如在 32 位系统上,std::has_single_bit(1ULL << 40)若传入unsigned int会截断,结果错误
真正要小心的是:它只回答“是不是”,不回答“在哪”;漏掉后续的 std::countr_zero 或等效操作,就只完成了一半任务。


















