Smart half edge iteration?
One possibility would be to create a bit array with 1 bit per half-edge. When you start iterating, initialize them all to 1, then clear the bits of each half-edge and its partner as you iterate.
The iteration can be done using __builtin_clz (GCC, clang)  or _BitScanReverse (MSVC)  [edit: or std::countl_zero in C++20!] to efficiently extract the next 1-bit without branching on every bit. Although you do then need a nested pair of loops, where you loop over the fields in your bit array, and then loop extracting bits until the current field is zero.