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 | }; |