Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

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