Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
| 3 | * It is copyright by its individual contributors, as recorded in the |
||
| 4 | * project's Git history. See COPYING.txt at the top level for license |
||
| 5 | * terms and a link to the Git history. |
||
| 6 | */ |
||
| 7 | #pragma once |
||
| 8 | |||
| 9 | #include <iterator> |
||
| 10 | #include <utility> |
||
| 11 | |||
| 12 | template <typename T> |
||
| 13 | class self_return_iterator : |
||
| 14 | T |
||
| 15 | { |
||
| 16 | public: |
||
| 17 | using iterator_category = std::forward_iterator_tag; |
||
| 18 | using value_type = T; |
||
| 19 | using difference_type = std::ptrdiff_t; |
||
| 20 | using pointer = T *; |
||
| 21 | using reference = T &; |
||
| 22 | self_return_iterator(T &&i) : |
||
| 23 | T(std::move(i)) |
||
| 24 | { |
||
| 25 | } |
||
| 26 | T base() const |
||
| 27 | { |
||
| 28 | return *this; |
||
| 29 | } |
||
| 30 | T operator*() const |
||
| 31 | { |
||
| 32 | /* This static_assert is eager: it will reject a type T that |
||
| 33 | * would be dangerous if used in the affected algorithms, |
||
| 34 | * regardless of whether the program attempts such a use. This |
||
| 35 | * is acceptable since the modification to fix this assertion |
||
| 36 | * should not break any intended uses of the type. To pass the |
||
| 37 | * assertion, the type T must define: |
||
| 38 | |||
| 39 | T &operator=(T &&) && = delete; |
||
| 40 | |||
| 41 | * If normal move assignment is desired, also define: |
||
| 42 | |||
| 43 | T &operator=(T &&) & = default; |
||
| 44 | |||
| 45 | */ |
||
| 46 | static_assert(!std::is_assignable<T &&, T &&>::value, "Accessibility of `T::operator=(T &&) &&` permits generation of incorrect code when passing self_return_iterator<T> to some algorithms. Explicitly delete `T::operator=(T &&) &&` to inhibit this assertion."); |
||
| 47 | return *this; |
||
| 48 | } |
||
| 49 | /* Some STL algorithms require: |
||
| 50 | * |
||
| 51 | * !!std::is_same<decltype(iter), decltype(++iter)>::value |
||
| 52 | * |
||
| 53 | * If this requirement is not met, template argument deduction |
||
| 54 | * fails when the algorithm calls a helper function. |
||
| 55 | * |
||
| 56 | * If not for this requirement, `using T::operator++` would have |
||
| 57 | * been sufficient here. |
||
| 58 | */ |
||
| 59 | self_return_iterator &operator++() |
||
| 60 | { |
||
| 61 | /* Use a static_cast instead of ignoring the return value and |
||
| 62 | * returning `*this`, to encourage the compiler to implement |
||
| 63 | * this as a tail-call when |
||
| 64 | * |
||
| 65 | * offsetof(self_return_iterator, T) == 0 |
||
| 66 | */ |
||
| 67 | return static_cast<self_return_iterator &>(this->T::operator++()); |
||
| 68 | } |
||
| 69 | /* operator++(int) is currently unused, but is required to satisfy |
||
| 70 | * the concept check on forward iterator. |
||
| 71 | */ |
||
| 72 | self_return_iterator operator++(int) |
||
| 73 | { |
||
| 74 | auto result = *this; |
||
| 75 | this->T::operator++(); |
||
| 76 | return result; |
||
| 77 | } |
||
| 78 | /* Since `T` is inherited privately, the base class `operator==` and |
||
| 79 | * `operator!=` cannot implicitly convert `rhs` to `T`. Define |
||
| 80 | * comparison operators to perform the conversion explicitly. |
||
| 81 | */ |
||
| 82 | bool operator==(const self_return_iterator &rhs) const |
||
| 83 | { |
||
| 84 | return this->T::operator==(static_cast<const T &>(rhs)); |
||
| 85 | } |
||
| 86 | bool operator!=(const self_return_iterator &rhs) const |
||
| 87 | { |
||
| 88 | return this->T::operator!=(static_cast<const T &>(rhs)); |
||
| 89 | } |
||
| 90 | }; |