Efficient way of iterating over true bits in stdbitset?
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
std::bitset is compact and fast for fixed-size bit flags, but iterating only the set bits can be tricky. A naive scan over all positions is simple and sometimes acceptable, yet sparse bitsets benefit from bit-twiddling approaches. This guide compares practical strategies and shows when each one is worth using.
Baseline: Scan Every Position
The simplest method is checking each index with test or operator[]. Complexity is linear in bitset size, independent of how many bits are set.
This is often good enough for small N or dense bitsets. It is also the easiest to maintain.
Faster Iteration for Small Fixed Width
If the bitset fits into an unsigned integer, you can extract that value and iterate set bits using x & -x style operations. This walks only set bits.
For sparse flags this is much faster because work scales with number of set bits, not total width.
General Approach for Larger std::bitset
std::bitset does not provide find_first and find_next APIs like some other containers. For very large compile-time sizes, a common pattern is storing data in machine-word chunks and iterating each chunk with trailing-zero operations.
If you control data representation, consider using an array of uint64_t for iteration-heavy workloads. You still get compact storage, but iteration logic is naturally set-bit oriented.
This pattern is common in schedulers, bitmap indexes, and simulation engines.
Choosing the Right Technique
Use full scan when:
- bitset size is small
- density is high
- readability matters more than micro-optimization
Use set-bit iteration when:
- bitset is sparse
- iteration is in a hot path
- you can operate in word chunks safely
Profile before rewriting. In many applications the simple scan is fast enough, and complexity cost of specialized bit hacks is not justified.
Portability Notes
Compiler intrinsics such as __builtin_ctzll are widely available on GCC and Clang. On C++20, std::countr_zero from bit header can be a cleaner standard alternative.
For cross-platform libraries, wrap intrinsic or standard calls behind a small utility function and centralize fallback behavior.
Common Pitfalls
- Assuming
std::bitsethas built-infind_nextstyle iteration helpers. - Using
to_ullongon bitsets wider than supported conversion range. - Forgetting that index ordering in printed bitset strings can be counterintuitive.
- Applying set-bit tricks without checking zero values before trailing-zero calls.
- Optimizing iteration prematurely without profiling actual hot paths.
Summary
- A full index scan is simplest and often sufficient for
std::bitset. - Sparse bitsets benefit from iterating set bits via word-level bit operations.
- For large workloads, chunked integer storage can outperform direct bitset scanning.
- Use portable wrappers for trailing-zero operations across compilers.
- Choose complexity level based on measured bottlenecks, not assumption.
Related reading
- Efficient way to compare two arrays
- Efficient way to compute geometric mean of many numbers
- Efficient way to compute number of hits to a server within the last minute, in real time
- Efficient way to filter out elements from stdvector
- Emulate double using 2 floats
- Erasing elements in stdvector by using indexes
- Efficient way to find degrees of separation between two nodes in a graph
- Efficient Way to Find Pair Orderings?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.