std::vector 不能当 Bitmap 用,因其 operator[] 返回代理对象而非 bool&,无 data() 接口,迭代器解引用返回临时对象,内部存储不保证对齐,且值域理解易错。

直接用 std::vector 实现 Bitmap 去重,线上跑着跑着就静默出错、查不到数、甚至崩溃——这不是你代码写错了,是它根本就不能这么用。
为什么 std::vector 不能当 Bitmap 用
它不是“布尔容器”,而是位压缩特化实现:
-
operator[]返回的是代理对象std::vector,不是::reference bool&,所以&v[i]编译失败,std::find_if等算法行为未定义 - 没有
data()接口,拿不到原始内存指针,无法做 SIMD、原子操作或 mmap 加载 - 迭代器解引用返回临时对象,开
-O2后可能被优化掉,v[i] = true看似生效,实际没写进内存 - 内部存储不保证字节对齐,
_mm256_load_si256这类指令会触发#GP异常
set() 和 test() 的边界检查必须写在函数内
Bitmap 构造参数是最大可接受值(即值域上限),不是数组长度。比如 BitMap(100) 表示支持 [0, 100] 范围内的整数,合法索引是 0 ~ 100(共 101 个),但内部数组只分配 (100 + 63) / 64 = 2 个 uint64_t。调用 set(100) 时,i / 64 = 1 合法,但若构造时误传 n = 100 却理解成“最多存 100 个数”,往里塞值为 100 的元素,就踩中了“值即索引”的前提漏洞。
- 务必在
set(size_t i)开头加if (i >= size_) return;,size_应为max_val + 1 -
test(size_t i)必须双检:i 且位操作结果有效,否则越界读会返回垃圾值 - 调试期建议用
std::vector替代::at(i >> 6) [],自动抛std::out_of_range
位索引计算必须用位运算,别用除法和取模
在热点路径里,i / 64 和 i % 64 会被编译器优化成位运算,但显式写出来更可控、更易向量化:
- 字索引统一用
i >> 6(64 位单元)或i >> 3(8 位单元) - 位偏移统一用
i & 63或i & 7,别写i % 64——x86 上仍可能走微码除法 - 左移常量必须带后缀:
1ULL ,否则1 是未定义行为 - 分配大小用
(max_val + 63) / 64向上取整,别用resize()动态扩,避免 memcpy 开销
超大值域(如 uint64_t)必须分层哈希降维
全量覆盖 2^64 需要 2 EB 内存,物理上不可能。真实方案永远是“局部存在性 + 可控假阳性”:
- 第一级:高位截取定桶,例如
bucket = (x >> 52) & 0xfff(4096 桶) - 第二级:低位哈希定偏移,用 MurmurHash3 截断到 20 位:
pos = fast_hash(x) & 0xfffff - 每个桶用
std::bitset(128 KB),总内存 ≈ 512 MB,可调 - 插入只置位,查询为 0 则一定不存在,为 1 则可能存在(假阳性可控)
最易被忽略的一点:所有映射都必须在 set() 和 test() 入口完成,不能指望上游数据“已经干净”。负数、超界值、NaN(若从浮点转来)必须当场丢弃或走 fallback 路径——位图不负责清洗,只负责高速判重。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











