Subversion Repositories Games.Descent

Rev

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