This does not work by a mere composition of enumerate and zip_equal,
because C++17 does not allow for recursive expansion of structured
This implementation uses zippy to manage the iteratees and adds the
stream of indices as the first zipped range. Because we have an upfront
assertion that all input ranges are of the same length, we only need to
check if the second range has ended during iteration.
As a consequence of using zippy, enumerate will now follow the
reference and lifetime semantics of the zip* family of functions. The
main difference is that enumerate exposes each tuple of references
through a new tuple-like type enumerate_result, with the familiar
.index() and .value() member functions.
Because the enumerate_result returned on dereference is a
temporary, enumeration result can no longer be used through an