Parcourir la source

view:
* split view iterator from multi-type view
* stable/packed storage policy support

Michele Caini il y a 4 ans
Parent
commit
2c35203647
3 fichiers modifiés avec 382 ajouts et 189 suppressions
  1. 1 1
      src/entt/entity/fwd.hpp
  2. 253 178
      src/entt/entity/view.hpp
  3. 128 10
      test/entt/entity/view.cpp

+ 1 - 1
src/entt/entity/fwd.hpp

@@ -22,7 +22,7 @@ class basic_registry;
 
 
 template<typename...>
-class basic_view;
+struct basic_view;
 
 
 template<typename>

+ 253 - 178
src/entt/entity/view.hpp

@@ -10,6 +10,7 @@
 #include <type_traits>
 #include "../config/config.h"
 #include "../core/type_traits.hpp"
+#include "component.hpp"
 #include "entity.hpp"
 #include "fwd.hpp"
 #include "sparse_set.hpp"
@@ -21,13 +22,153 @@ namespace entt {
 
 
 /**
- * @brief View.
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+
+namespace internal {
+
+
+template<typename Policy, typename It, std::size_t AllOf, std::size_t NoneOf>
+class view_iterator final {
+    using basic_common_type = basic_sparse_set<typename std::iterator_traits<It>::value_type>;
+
+    [[nodiscard]] bool valid() const {
+        const auto entt = *it;
+        return Policy::accept(entt)
+            && std::apply([entt](const auto *... curr) { return (curr->contains(entt) && ...); }, pools)
+            && std::apply([entt](const auto *... curr) { return (!curr->contains(entt) && ...); }, filter);
+    }
+
+public:
+    using difference_type = typename std::iterator_traits<It>::difference_type;
+    using value_type = typename std::iterator_traits<It>::value_type;
+    using pointer = typename std::iterator_traits<It>::pointer;
+    using reference = typename std::iterator_traits<It>::reference;
+    using iterator_category = std::bidirectional_iterator_tag;
+
+    view_iterator() ENTT_NOEXCEPT
+        : first{},
+          last{},
+          it{},
+          pools{},
+          filter{}
+    {}
+
+    view_iterator(It from, It to, It curr, std::array<const basic_common_type *, AllOf> all_of, std::array<const basic_common_type *, NoneOf> none_of) ENTT_NOEXCEPT
+        : first{from},
+          last{to},
+          it{curr},
+          pools{all_of},
+          filter{none_of}
+    {
+        if(it != last && !valid()) {
+            ++(*this);
+        }
+    }
+
+    view_iterator & operator++() ENTT_NOEXCEPT {
+        while(++it != last && !valid());
+        return *this;
+    }
+
+    view_iterator operator++(int) ENTT_NOEXCEPT {
+        view_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    view_iterator & operator--() ENTT_NOEXCEPT {
+        while(--it != first && !valid());
+        return *this;
+    }
+
+    view_iterator operator--(int) ENTT_NOEXCEPT {
+        view_iterator orig = *this;
+        return operator--(), orig;
+    }
+
+    [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
+        return other.it == it;
+    }
+
+    [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
+        return !(*this == other);
+    }
+
+    [[nodiscard]] pointer operator->() const {
+        return &*it;
+    }
+
+    [[nodiscard]] reference operator*() const {
+        return *operator->();
+    }
+
+private:
+    It first;
+    It last;
+    It it;
+    std::array<const basic_common_type *, AllOf> pools;
+    std::array<const basic_common_type *, NoneOf> filter;
+};
+
+
+}
+
+
+/*! @brief Stable storage policy, aimed at pointer stability. */
+struct stable_storage_policy {
+    /**
+    * @cond TURN_OFF_DOXYGEN
+    * Internal details not to be documented.
+    */
+    template<typename Entity>
+    [[nodiscard]] static constexpr bool accept(const Entity entity) ENTT_NOEXCEPT {
+        return entity != tombstone;
+    }
+    /**
+    * Internal details not to be documented.
+    * @endcond
+    */
+};
+
+
+/*! @brief Packed storage policy, aimed at faster linear iteration. */
+struct packed_storage_policy {
+    /**
+    * @cond TURN_OFF_DOXYGEN
+    * Internal details not to be documented.
+    */
+    template<typename Entity>
+    [[nodiscard]] static constexpr bool accept(const Entity) ENTT_NOEXCEPT {
+        return true;
+    }
+    /**
+    * Internal details not to be documented.
+    * @endcond
+    */
+};
+
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
+
+/**
+ * @brief View implementation.
  *
  * Primary template isn't defined on purpose. All the specializations give a
  * compile-time error, but for a few reasonable cases.
  */
 template<typename...>
-class basic_view;
+class basic_view_impl;
+
+
+/*! @brief View implementation dispatcher. */
+template<typename...>
+struct basic_view;
 
 
 /**
@@ -59,120 +200,38 @@ class basic_view;
  * Lifetime of a view must not overcome that of the registry that generated it.
  * In any other case, attempting to use a view results in undefined behavior.
  *
+ * @tparam Policy Common (stricter) storage policy.
  * @tparam Entity A valid entity type (see entt_traits for more details).
  * @tparam Exclude Types of components used to filter the view.
  * @tparam Component Types of components iterated by the view.
  */
-template<typename Entity, typename... Exclude, typename... Component>
-class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
+template<typename Policy, typename Entity, typename... Exclude, typename... Component>
+class basic_view_impl<Policy, Entity, exclude_t<Exclude...>, Component...> {
+    using basic_common_type = basic_sparse_set<Entity>;
+
     template<typename Comp>
     using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;
 
-    using unchecked_type = std::array<const basic_sparse_set<Entity> *, (sizeof...(Component) - 1)>;
-
-    template<typename It>
-    class view_iterator final {
-        friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
-
-        view_iterator(It from, It to, It curr, unchecked_type other, const std::tuple<const storage_type<Exclude> *...> &ignore) ENTT_NOEXCEPT
-            : first{from},
-              last{to},
-              it{curr},
-              unchecked{other},
-              filter{ignore}
-        {
-            if(it != last && !valid()) {
-                ++(*this);
-            }
-        }
-
-        [[nodiscard]] bool valid() const {
-            const auto entt = *it;
-
-            return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt](const basic_sparse_set<Entity> *curr) { return curr->contains(entt); })
-                && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...);
-        }
-
-    public:
-        using difference_type = typename std::iterator_traits<It>::difference_type;
-        using value_type = typename std::iterator_traits<It>::value_type;
-        using pointer = typename std::iterator_traits<It>::pointer;
-        using reference = typename std::iterator_traits<It>::reference;
-        using iterator_category = std::bidirectional_iterator_tag;
-
-        view_iterator() ENTT_NOEXCEPT
-            : view_iterator{{}, {}, {}, {}, {}}
-        {}
-
-        view_iterator & operator++() ENTT_NOEXCEPT {
-            while(++it != last && !valid());
-            return *this;
-        }
-
-        view_iterator operator++(int) ENTT_NOEXCEPT {
-            view_iterator orig = *this;
-            return ++(*this), orig;
-        }
-
-        view_iterator & operator--() ENTT_NOEXCEPT {
-            while(--it != first && !valid());
-            return *this;
-        }
-
-        view_iterator operator--(int) ENTT_NOEXCEPT {
-            view_iterator orig = *this;
-            return operator--(), orig;
-        }
-
-        [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT {
-            return other.it == it;
-        }
-
-        [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT {
-            return !(*this == other);
-        }
-
-        [[nodiscard]] pointer operator->() const {
-            return &*it;
-        }
-
-        [[nodiscard]] reference operator*() const {
-            return *operator->();
-        }
-
-    private:
-        It first;
-        It last;
-        It it;
-        unchecked_type unchecked;
-        std::tuple<const storage_type<Exclude> *...> filter;
-    };
-
-    class iterable_view final {
-        friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
-
+    class iterable final {
         template<typename It>
-        class iterable_view_iterator final {
-            friend class iterable_view;
-
-            iterable_view_iterator(It from, const basic_view *parent) ENTT_NOEXCEPT
-                : it{from},
-                  view{parent}
-            {}
-
-        public:
+        struct iterable_iterator final {
             using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view>().get({})));
+            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view_impl>().get({})));
             using pointer = void;
             using reference = value_type;
             using iterator_category = std::input_iterator_tag;
 
-            iterable_view_iterator & operator++() ENTT_NOEXCEPT {
+            iterable_iterator(It from, const basic_view_impl *parent) ENTT_NOEXCEPT
+                : it{from},
+                  view{parent}
+            {}
+
+            iterable_iterator & operator++() ENTT_NOEXCEPT {
                 return ++it, *this;
             }
 
-            iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
-                iterable_view_iterator orig = *this;
+            iterable_iterator operator++(int) ENTT_NOEXCEPT {
+                iterable_iterator orig = *this;
                 return ++(*this), orig;
             }
 
@@ -180,27 +239,27 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
                 return std::tuple_cat(std::make_tuple(*it), view->get(*it));
             }
 
-            [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
+            [[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
                 return other.it == it;
             }
 
-            [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
+            [[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
                 return !(*this == other);
             }
 
         private:
             It it;
-            const basic_view *view;
+            const basic_view_impl *view;
         };
 
-        iterable_view(const basic_view &parent)
+    public:
+        using iterator = iterable_iterator<internal::view_iterator<Policy, typename basic_common_type::iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>>;
+        using reverse_iterator = iterable_iterator<internal::view_iterator<Policy, typename basic_common_type::reverse_iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>>;
+
+        iterable(const basic_view_impl &parent)
             : view{parent}
         {}
 
-    public:
-        using iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::iterator>>;
-        using reverse_iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>>;
-
         [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
             return { view.begin(), &view };
         }
@@ -218,22 +277,26 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
         }
 
     private:
-        const basic_view view;
+        const basic_view_impl view;
     };
 
-    [[nodiscard]] const basic_sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
-        return (std::min)({ static_cast<const basic_sparse_set<entity_type> *>(std::get<storage_type<Component> *>(pools))... }, [](const auto *lhs, const auto *rhs) {
+    [[nodiscard]] const auto * candidate() const ENTT_NOEXCEPT {
+        return (std::min)({ static_cast<const basic_common_type *>(std::get<storage_type<Component> *>(pools))... }, [](const auto *lhs, const auto *rhs) {
             return lhs->size() < rhs->size();
         });
     }
 
-    [[nodiscard]] unchecked_type unchecked(const basic_sparse_set<Entity> *cpool) const {
+    [[nodiscard]] auto pools_to_unchecked_array() const ENTT_NOEXCEPT {
         std::size_t pos{};
-        unchecked_type other{};
-        (static_cast<void>(std::get<storage_type<Component> *>(pools) == cpool ? void() : void(other[pos++] = std::get<storage_type<Component> *>(pools))), ...);
+        std::array<const basic_common_type *, sizeof...(Component) - 1u> other{};
+        (static_cast<void>(std::get<storage_type<Component> *>(pools) == view ? void() : void(other[pos++] = std::get<storage_type<Component> *>(pools))), ...);
         return other;
     }
 
+    [[nodiscard]] auto filter_to_array() const ENTT_NOEXCEPT {
+        return std::array<const basic_common_type *, sizeof...(Exclude)>{std::get<const storage_type<Exclude> *>(filter)...};
+    }
+
     template<typename Comp, typename It>
     [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const {
         if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, typename storage_type<Comp>::value_type>) {
@@ -246,11 +309,11 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
     template<typename Comp, typename Func>
     void traverse(Func func) const {
         if constexpr(std::is_void_v<decltype(std::get<storage_type<Comp> *>(pools)->get({}))>) {
-            for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
-                if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
-                    && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
+            for(const auto entt: static_cast<const basic_common_type &>(*std::get<storage_type<Comp> *>(pools))) {
+                if(Policy::accept(entt) && ((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
+                    && (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...))
                 {
-                    if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
+                    if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view_impl>().get({})))>) {
                         std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt)));
                     } else {
                         std::apply(func, get(entt));
@@ -260,11 +323,11 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
         } else {
             auto it = std::get<storage_type<Comp> *>(pools)->begin();
 
-            for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
-                if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
-                    && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
+            for(const auto entt: static_cast<const basic_common_type &>(*std::get<storage_type<Comp> *>(pools))) {
+                if(Policy::accept(entt) && ((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
+                    && (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...))
                 {
-                    if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) {
+                    if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view_impl>().get({})))>) {
                         std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Component>(it, entt)...));
                     } else {
                         std::apply(func, std::tuple_cat(dispatch_get<Component>(it, entt)...));
@@ -282,12 +345,14 @@ public:
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
     /*! @brief Bidirectional iterator type. */
-    using iterator = view_iterator<typename basic_sparse_set<entity_type>::iterator>;
+    using iterator = internal::view_iterator<Policy, typename basic_common_type::iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>;
     /*! @brief Reverse iterator type. */
-    using reverse_iterator = view_iterator<typename basic_sparse_set<entity_type>::reverse_iterator>;
+    using reverse_iterator = internal::view_iterator<Policy, typename basic_common_type::reverse_iterator, sizeof...(Component) - 1u, sizeof...(Exclude)>;
+    /*! @brief Iterable view type. */
+    using iterable_view = iterable;
 
     /*! @brief Default constructor to use to create empty, invalid views. */
-    basic_view() ENTT_NOEXCEPT
+    basic_view_impl() ENTT_NOEXCEPT
         : view{}
     {}
 
@@ -296,7 +361,7 @@ public:
      * @param component The storage for the types to iterate.
      * @param epool The storage for the types used to filter the view.
      */
-    basic_view(storage_type<Component> &... component, const storage_type<Exclude> &... epool) ENTT_NOEXCEPT
+    basic_view_impl(storage_type<Component> &... component, const storage_type<Exclude> &... epool) ENTT_NOEXCEPT
         : pools{&component...},
           filter{&epool...},
           view{candidate()}
@@ -328,7 +393,7 @@ public:
      * @return An iterator to the first entity of the view.
      */
     [[nodiscard]] iterator begin() const {
-        return iterator{view->begin(), view->end(), view->begin(), unchecked(view), filter};
+        return iterator{view->begin(), view->end(), view->begin(), pools_to_unchecked_array(), filter_to_array()};
     }
 
     /**
@@ -341,7 +406,7 @@ public:
      * @return An iterator to the entity following the last entity of the view.
      */
     [[nodiscard]] iterator end() const {
-        return iterator{view->begin(), view->end(), view->end(), unchecked(view), filter};
+        return iterator{view->begin(), view->end(), view->end(), pools_to_unchecked_array(), filter_to_array()};
     }
 
     /**
@@ -353,7 +418,7 @@ public:
      * @return An iterator to the first entity of the reversed view.
      */
     [[nodiscard]] reverse_iterator rbegin() const {
-        return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), unchecked(view), filter};
+        return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), pools_to_unchecked_array(), filter_to_array()};
     }
 
     /**
@@ -368,7 +433,7 @@ public:
      * reversed view.
      */
     [[nodiscard]] reverse_iterator rend() const {
-        return reverse_iterator{view->rbegin(), view->rend(), view->rend(), unchecked(view), filter};
+        return reverse_iterator{view->rbegin(), view->rend(), view->rend(), pools_to_unchecked_array(), filter_to_array()};
     }
 
     /**
@@ -398,7 +463,7 @@ public:
      * iterator otherwise.
      */
     [[nodiscard]] iterator find(const entity_type entt) const {
-        const auto it = iterator{view->begin(), view->end(), view->find(entt), unchecked(view), filter};
+        const auto it = iterator{view->begin(), view->end(), view->find(entt), pools_to_unchecked_array(), filter_to_array()};
         return (it != end() && *it == entt) ? it : end();
     }
 
@@ -416,7 +481,7 @@ public:
      * @return True if the view contains the given entity, false otherwise.
      */
     [[nodiscard]] bool contains(const entity_type entt) const {
-        return (std::get<storage_type<Component> *>(pools)->contains(entt) && ...) && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...);
+        return (std::get<storage_type<Component> *>(pools)->contains(entt) && ...) && (!std::get<const storage_type<Exclude> *>(filter)->contains(entt) && ...);
     }
 
     /**
@@ -480,9 +545,7 @@ public:
      *
      * The pool of the suggested component is used to lead the iterations. The
      * returned entities will therefore respect the order of the pool associated
-     * with that type.<br/>
-     * It is no longer guaranteed that the performance is the best possible, but
-     * there will be greater control over the order of iteration.
+     * with that type.
      *
      * @sa each
      *
@@ -518,9 +581,7 @@ public:
      *
      * The pool of the suggested component is used to lead the iterations. The
      * returned elements will therefore respect the order of the pool associated
-     * with that type.<br/>
-     * It is no longer guaranteed that the performance is the best possible, but
-     * there will be greater control over the order of iteration.
+     * with that type.
      *
      * @sa each
      *
@@ -548,7 +609,7 @@ public:
 private:
     const std::tuple<storage_type<Component> *...> pools;
     const std::tuple<const storage_type<Exclude> *...> filter;
-    mutable const basic_sparse_set<entity_type> *view;
+    mutable const basic_common_type *view;
 };
 
 
@@ -584,34 +645,30 @@ private:
  * @tparam Component Type of component iterated by the view.
  */
 template<typename Entity, typename Component>
-class basic_view<Entity, exclude_t<>, Component> final {
+class basic_view_impl<packed_storage_policy, Entity, exclude_t<>, Component> {
+    using basic_common_type = basic_sparse_set<Entity>;
     using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
 
-    class iterable_view final {
-        friend class basic_view<Entity, exclude_t<>, Component>;
-
+    class iterable final {
         template<typename... It>
-        class iterable_view_iterator final {
-            friend class iterable_view;
-
-            template<typename... Discard>
-            iterable_view_iterator(It... from, Discard...) ENTT_NOEXCEPT
-                : it{from...}
-            {}
-
-        public:
+        struct iterable_iterator final {
             using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view>().get({})));
+            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view_impl>().get({})));
             using pointer = void;
             using reference = value_type;
             using iterator_category = std::input_iterator_tag;
 
-            iterable_view_iterator & operator++() ENTT_NOEXCEPT {
+            template<typename... Discard>
+            iterable_iterator(It... from, Discard...) ENTT_NOEXCEPT
+                : it{from...}
+            {}
+
+            iterable_iterator & operator++() ENTT_NOEXCEPT {
                 return (++std::get<It>(it), ...), *this;
             }
 
-            iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
-                iterable_view_iterator orig = *this;
+            iterable_iterator operator++(int) ENTT_NOEXCEPT {
+                iterable_iterator orig = *this;
                 return ++(*this), orig;
             }
 
@@ -619,11 +676,11 @@ class basic_view<Entity, exclude_t<>, Component> final {
                 return { *std::get<It>(it)... };
             }
 
-            [[nodiscard]] bool operator==(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
+            [[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT {
                 return std::get<0>(other.it) == std::get<0>(it);
             }
 
-            [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
+            [[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT {
                 return !(*this == other);
             }
 
@@ -631,36 +688,36 @@ class basic_view<Entity, exclude_t<>, Component> final {
             std::tuple<It...> it;
         };
 
-        iterable_view(storage_type &ref)
-            : pool{&ref}
-        {}
-
     public:
         using iterator = std::conditional_t<
             std::is_void_v<decltype(std::declval<storage_type>().get({}))>,
-            iterable_view_iterator<typename basic_sparse_set<Entity>::iterator>,
-            iterable_view_iterator<typename basic_sparse_set<Entity>::iterator, decltype(std::declval<storage_type>().begin())>
+            iterable_iterator<typename basic_common_type::iterator>,
+            iterable_iterator<typename basic_common_type::iterator, decltype(std::declval<storage_type>().begin())>
         >;
         using reverse_iterator = std::conditional_t<
             std::is_void_v<decltype(std::declval<storage_type>().get({}))>,
-            iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>,
-            iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator, decltype(std::declval<storage_type>().rbegin())>
+            iterable_iterator<typename basic_common_type::reverse_iterator>,
+            iterable_iterator<typename basic_common_type::reverse_iterator, decltype(std::declval<storage_type>().rbegin())>
         >;
 
+        iterable(storage_type &ref)
+            : pool{&ref}
+        {}
+
         [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-            return iterator{pool->basic_sparse_set<entity_type>::begin(), pool->begin()};
+            return iterator{pool->basic_common_type::begin(), pool->begin()};
         }
 
         [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-            return iterator{pool->basic_sparse_set<entity_type>::end(), pool->end()};
+            return iterator{pool->basic_common_type::end(), pool->end()};
         }
 
         [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
-            return reverse_iterator{pool->basic_sparse_set<entity_type>::rbegin(), pool->rbegin()};
+            return reverse_iterator{pool->basic_common_type::rbegin(), pool->rbegin()};
         }
 
         [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
-            return reverse_iterator{pool->basic_sparse_set<entity_type>::rend(), pool->rend()};
+            return reverse_iterator{pool->basic_common_type::rend(), pool->rend()};
         }
 
     private:
@@ -673,12 +730,14 @@ public:
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
     /*! @brief Random access iterator type. */
-    using iterator = typename basic_sparse_set<Entity>::iterator;
+    using iterator = typename basic_common_type::iterator;
     /*! @brief Reversed iterator type. */
-    using reverse_iterator = typename basic_sparse_set<Entity>::reverse_iterator;
+    using reverse_iterator = typename basic_common_type::reverse_iterator;
+    /*! @brief Iterable view type. */
+    using iterable_view = iterable;
 
     /*! @brief Default constructor to use to create empty, invalid views. */
-    basic_view() ENTT_NOEXCEPT
+    basic_view_impl() ENTT_NOEXCEPT
         : pools{},
           filter{}
     {}
@@ -687,7 +746,7 @@ public:
      * @brief Constructs a single-type view from a storage class.
      * @param ref The storage for the type to iterate.
      */
-    basic_view(storage_type &ref) ENTT_NOEXCEPT
+    basic_view_impl(storage_type &ref) ENTT_NOEXCEPT
         : pools{&ref},
           filter{}
     {}
@@ -737,7 +796,7 @@ public:
      * @return An iterator to the first entity of the view.
      */
     [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-        return std::get<0>(pools)->basic_sparse_set<entity_type>::begin();
+        return std::get<0>(pools)->basic_common_type::begin();
     }
 
     /**
@@ -750,7 +809,7 @@ public:
      * @return An iterator to the entity following the last entity of the view.
      */
     [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-        return std::get<0>(pools)->basic_sparse_set<entity_type>::end();
+        return std::get<0>(pools)->basic_common_type::end();
     }
 
     /**
@@ -762,7 +821,7 @@ public:
      * @return An iterator to the first entity of the reversed view.
      */
     [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
-        return std::get<0>(pools)->basic_sparse_set<entity_type>::rbegin();
+        return std::get<0>(pools)->basic_common_type::rbegin();
     }
 
     /**
@@ -777,7 +836,7 @@ public:
      * reversed view.
      */
     [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
-        return std::get<0>(pools)->basic_sparse_set<entity_type>::rend();
+        return std::get<0>(pools)->basic_common_type::rend();
     }
 
     /**
@@ -946,6 +1005,22 @@ private:
 };
 
 
+/**
+ * @brief View implementation dispatcher.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Exclude Types of components used to filter the view.
+ * @tparam Component Types of components iterated by the view.
+ */
+template<typename Entity, typename... Exclude, typename... Component>
+struct basic_view<Entity, exclude_t<Exclude...>, Component...>
+    : basic_view_impl<std::conditional_t<std::disjunction_v<typename component_traits<std::remove_const_t<Component>>::in_place_delete...>, stable_storage_policy, packed_storage_policy>, Entity, exclude_t<Exclude...>, Component...>
+{
+    /*! @brief Most restrictive storage policy of all component types. */
+    using storage_policy = std::conditional_t<std::disjunction_v<typename component_traits<std::remove_const_t<Component>>::in_place_delete...>, stable_storage_policy, packed_storage_policy>;
+    using basic_view_impl<storage_policy, Entity, exclude_t<Exclude...>, Component...>::basic_view_impl;
+};
+
+
 /**
  * @brief Deduction guide.
  * @tparam Storage Type of storage classes used to create the view.

+ 128 - 10
test/entt/entity/view.cpp

@@ -7,6 +7,13 @@
 #include <entt/entity/view.hpp>
 
 struct empty_type {};
+struct stable_type { int value; };
+
+template<>
+struct entt::component_traits<stable_type> {
+    using in_place_delete = std::true_type;
+    using ignore_if_empty = std::true_type;
+};
 
 TEST(SingleComponentView, Functionalities) {
     entt::registry registry;
@@ -378,10 +385,16 @@ TEST(SingleComponentView, FrontBack) {
 
 TEST(SingleComponentView, DeductionGuide) {
     entt::registry registry;
-    typename entt::storage_traits<entt::entity, int>::storage_type storage;
+    typename entt::storage_traits<entt::entity, int>::storage_type istorage;
+    typename entt::storage_traits<entt::entity, stable_type>::storage_type sstorage;
 
-    static_assert(std::is_same_v<decltype(entt::basic_view{storage}), entt::basic_view<entt::entity, entt::exclude_t<>, int>>);
-    static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(storage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int>>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int>, decltype(entt::basic_view{istorage})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int>, decltype(entt::basic_view{std::as_const(istorage)})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, stable_type>, decltype(entt::basic_view{sstorage})>);
+
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int>, decltype(entt::basic_view{istorage})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int>, decltype(entt::basic_view{std::as_const(istorage)})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<>, stable_type>, decltype(entt::basic_view{sstorage})>);
 }
 
 TEST(SingleComponentView, IterableViewAlgorithmCompatibility) {
@@ -397,6 +410,47 @@ TEST(SingleComponentView, IterableViewAlgorithmCompatibility) {
     ASSERT_EQ(std::get<0>(*it), entity);
 }
 
+TEST(SingleComponentView, StableType) {
+    entt::registry registry;
+    auto view = registry.view<stable_type>();
+
+    const auto entity = registry.create();
+    const auto other = registry.create();
+
+    registry.emplace<stable_type>(entity);
+    registry.emplace<stable_type>(other);
+    registry.destroy(entity);
+
+    ASSERT_EQ(view.size_hint(), 2u);
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(view.contains(other));
+
+    ASSERT_EQ(view.front(), other);
+    ASSERT_EQ(view.back(), other);
+
+    ASSERT_EQ(*view.begin(), other);
+    ASSERT_EQ(++view.begin(), view.end());
+
+    view.each([other](const auto entt, stable_type) {
+        ASSERT_EQ(other, entt);
+    });
+
+    view.each([check = true](stable_type) mutable {
+        ASSERT_TRUE(check);
+        check = false;
+    });
+
+    for(auto [entt, st]: view.each()) {
+        static_assert(std::is_same_v<decltype(entt), entt::entity>);
+        static_assert(std::is_same_v<decltype(st), stable_type &>);
+        ASSERT_EQ(other, entt);
+    }
+
+    registry.compact();
+
+    ASSERT_EQ(view.size_hint(), 1u);
+}
+
 TEST(MultiComponentView, Functionalities) {
     entt::registry registry;
     auto view = registry.view<int, char>();
@@ -937,11 +991,19 @@ TEST(MultiComponentView, DeductionGuide) {
     entt::registry registry;
     typename entt::storage_traits<entt::entity, int>::storage_type istorage;
     typename entt::storage_traits<entt::entity, double>::storage_type dstorage;
-
-    static_assert(std::is_same_v<decltype(entt::basic_view{istorage, dstorage}), entt::basic_view<entt::entity, entt::exclude_t<>, int, double>>);
-    static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(istorage), dstorage}), entt::basic_view<entt::entity, entt::exclude_t<>, const int, double>>);
-    static_assert(std::is_same_v<decltype(entt::basic_view{istorage, std::as_const(dstorage)}), entt::basic_view<entt::entity, entt::exclude_t<>, int, const double>>);
-    static_assert(std::is_same_v<decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)}), entt::basic_view<entt::entity, entt::exclude_t<>, const int, const double>>);
+    typename entt::storage_traits<entt::entity, stable_type>::storage_type sstorage;
+
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, double>, decltype(entt::basic_view{istorage, dstorage})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int, double>, decltype(entt::basic_view{std::as_const(istorage), dstorage})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, const double>, decltype(entt::basic_view{istorage, std::as_const(dstorage)})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, const int, const double>, decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)})>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<>, int, stable_type>, decltype(entt::basic_view{istorage, sstorage})>);
+
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int, double>, decltype(entt::basic_view{istorage, dstorage})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int, double>, decltype(entt::basic_view{std::as_const(istorage), dstorage})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, int, const double>, decltype(entt::basic_view{istorage, std::as_const(dstorage)})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<>, const int, const double>, decltype(entt::basic_view{std::as_const(istorage), std::as_const(dstorage)})>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<>, int, stable_type>, decltype(entt::basic_view{istorage, sstorage})>);
 }
 
 TEST(MultiComponentView, IterableViewAlgorithmCompatibility) {
@@ -958,6 +1020,54 @@ TEST(MultiComponentView, IterableViewAlgorithmCompatibility) {
     ASSERT_EQ(std::get<0>(*it), entity);
 }
 
+TEST(MultiComponentView, StableType) {
+    entt::registry registry;
+    auto view = registry.view<int, stable_type>();
+
+    const auto entity = registry.create();
+    const auto other = registry.create();
+
+    registry.emplace<int>(entity);
+    registry.emplace<int>(other);
+    registry.emplace<stable_type>(entity);
+    registry.emplace<stable_type>(other);
+    registry.destroy(entity);
+
+    ASSERT_EQ(view.size_hint(), 1u);
+
+    view.use<stable_type>();
+
+    ASSERT_EQ(view.size_hint(), 2u);
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(view.contains(other));
+
+    ASSERT_EQ(view.front(), other);
+    ASSERT_EQ(view.back(), other);
+
+    ASSERT_EQ(*view.begin(), other);
+    ASSERT_EQ(++view.begin(), view.end());
+
+    view.each([other](const auto entt, int, stable_type) {
+        ASSERT_EQ(other, entt);
+    });
+
+    view.each([check = true](int, stable_type) mutable {
+        ASSERT_TRUE(check);
+        check = false;
+    });
+
+    for(auto [entt, iv, st]: view.each()) {
+        static_assert(std::is_same_v<decltype(entt), entt::entity>);
+        static_assert(std::is_same_v<decltype(iv), int &>);
+        static_assert(std::is_same_v<decltype(st), stable_type &>);
+        ASSERT_EQ(other, entt);
+    }
+
+    registry.compact();
+
+    ASSERT_EQ(view.size_hint(), 1u);
+}
+
 TEST(View, Pipe) {
     entt::registry registry;
     const auto entity = registry.create();
@@ -970,15 +1080,20 @@ TEST(View, Pipe) {
 
     registry.emplace<int>(other);
     registry.emplace<char>(other);
+    registry.emplace<stable_type>(other);
 
     const auto view1 = registry.view<int>(entt::exclude<double>);
     const auto view2 = registry.view<const char>(entt::exclude<float>);
     const auto view3 = registry.view<empty_type>();
+    const auto view4 = registry.view<stable_type>();
 
-    static_assert(std::is_same_v<decltype(view1 | view2), entt::basic_view<entt::entity, entt::exclude_t<double, float>, int, const char>>);
-    static_assert(std::is_same_v<decltype(view2 | view1), entt::basic_view<entt::entity, entt::exclude_t<float, double>, const char, int>>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<double, float>, int, const char>, decltype(view1 | view2)>);
+    static_assert(std::is_same_v<entt::basic_view<entt::entity, entt::exclude_t<float, double>, const char, int>, decltype(view2 | view1)>);
     static_assert(std::is_same_v<decltype((view1 | view2) | view3), decltype(view1 | (view2 | view3))>);
 
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::packed_storage_policy, entt::entity, entt::exclude_t<double, float>, int, const char>, decltype(view1 | view2)>);
+    static_assert(std::is_base_of_v<entt::basic_view_impl<entt::stable_storage_policy, entt::entity, entt::exclude_t<double, float>, int, stable_type, const char>, decltype(view1 | view4 | view2)>);
+
     ASSERT_FALSE((view1 | view2).contains(entity));
     ASSERT_TRUE((view1 | view2).contains(other));
 
@@ -987,4 +1102,7 @@ TEST(View, Pipe) {
 
     ASSERT_FALSE((view1 | view2 | view3).contains(entity));
     ASSERT_FALSE((view1 | view2 | view3).contains(other));
+
+    ASSERT_FALSE((view1 | view4 | view2).contains(entity));
+    ASSERT_TRUE((view1 | view4 | view2).contains(other));
 }