Ver Fonte

core/entity/meta/poly:
* updated poly (static vtable, design review)
* .ref -> as_ref (any, meta_any, poly)
* poly storage

Michele Caini há 5 anos atrás
pai
commit
a974235d10

+ 13 - 12
src/entt/core/any.hpp

@@ -13,7 +13,7 @@
 namespace entt {
 
 
-/*! @brief A type-safe container for single values of any type. */
+/*! @brief A SBO friendly, type-safe container for single values of any type. */
 class any {
     enum class operation { COPY, MOVE, DTOR, ADDR, REF, TYPE };
 
@@ -126,7 +126,7 @@ public:
      * @param value An instance of an object to use to initialize the wrapper.
      */
     template<typename Type>
-    any(std::reference_wrapper<Type> value)
+    any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
         : vtable{&basic_vtable<std::add_lvalue_reference_t<Type>>},
           instance{&value.get()}
     {}
@@ -212,16 +212,6 @@ public:
         *this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
     }
 
-    /**
-     * @brief Aliasing constructor.
-     * @return An any that shares a reference to an unmanaged object.
-     */
-    [[nodiscard]] any ref() const ENTT_NOEXCEPT {
-        any other{};
-        vtable(operation::REF, *this, &other);
-        return other;
-    }
-
     /**
      * @brief Returns false if a wrapper is empty, true otherwise.
      * @return False if the wrapper is empty, true otherwise.
@@ -243,6 +233,17 @@ public:
         std::swap(lhs.vtable, rhs.vtable);
     }
 
+    /**
+     * @brief Aliasing constructor.
+     * @param other A reference to an object that isn't necessarily initialized.
+     * @return An any that shares a reference to an unmanaged object.
+     */
+    [[nodiscard]] friend any as_ref(const any &other) ENTT_NOEXCEPT {
+        any ref{};
+        other.vtable(operation::REF, other, &ref);
+        return ref;
+    }
+
 private:
     vtable_type *vtable;
     union { void *instance; storage_type storage; };

+ 83 - 0
src/entt/entity/poly_storage.hpp

@@ -0,0 +1,83 @@
+#ifndef ENTT_ENTITY_POLY_STORAGE_HPP
+#define ENTT_ENTITY_POLY_STORAGE_HPP
+
+
+#include <cstddef>
+#include <tuple>
+#include "../core/type_info.hpp"
+#include "../core/type_traits.hpp"
+#include "../poly/poly.hpp"
+#include "fwd.hpp"
+
+
+namespace entt {
+
+
+/**
+ * @brief Basic poly storage implementation.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity>
+struct Storage: type_list<
+    type_info() const ENTT_NOEXCEPT,
+    void(basic_registry<Entity> &, const Entity *, const Entity *)
+> {
+    /*! @brief Underlying entity identifier. */
+    using entity_type = Entity;
+    /*! @brief Unsigned integer type. */
+    using size_type = std::size_t;
+
+    /**
+     * @brief Concept definition.
+     * @tparam Base Opaque base class from which to inherit.
+     */
+    template<typename Base>
+    struct type: Base {
+        /**
+         * @brief Returns a type info for the contained objects.
+         * @return The type info for the contained objects.
+         */
+        type_info value_type() const ENTT_NOEXCEPT {
+            return poly_call<0>(*this);
+        }
+
+        /**
+         * @brief Removes entities from a storage.
+         * @param owner The registry that issued the request.
+         * @param first An iterator to the first element of the range of
+         * entities.
+         * @param last An iterator past the last element of the range of
+         * entities.
+         */
+        void remove(basic_registry<entity_type> &owner, const entity_type *first, const entity_type *last) {
+            poly_call<1>(*this, owner, first, last);
+        }
+    };
+
+    /**
+     * @brief Concept implementation.
+     * @tparam Type Type for which to generate an implementation.
+     */
+    template<typename Type>
+    using impl = value_list<
+        &type_id<typename Type::value_type>,
+        &Type::template remove<const entity_type *>
+    >;
+};
+
+
+/**
+ * @brief Defines the poly storage type associate with a given entity type.
+ * @tparam Entity A valid entity type (see entt_traits for more details).
+ */
+template<typename Entity, typename = void>
+struct poly_storage_traits {
+    /*! @brief Poly storage type for the given entity type. */
+    using storage_type = poly<Storage<Entity>>;
+};
+
+
+}
+
+
+#endif

+ 23 - 13
src/entt/entity/registry.hpp

@@ -4,6 +4,7 @@
 
 #include <algorithm>
 #include <cstddef>
+#include <functional>
 #include <iterator>
 #include <memory>
 #include <tuple>
@@ -18,6 +19,7 @@
 #include "entity.hpp"
 #include "fwd.hpp"
 #include "group.hpp"
+#include "poly_storage.hpp"
 #include "runtime_view.hpp"
 #include "sparse_set.hpp"
 #include "storage.hpp"
@@ -41,14 +43,14 @@ namespace entt {
 template<typename Entity>
 class basic_registry {
     using traits_type = entt_traits<Entity>;
+    using poly_storage_type = typename poly_storage_traits<Entity>::storage_type;
 
     template<typename Component>
     using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Component>>::storage_type, Component>;
 
     struct pool_data {
-        type_info info{};
+        poly_storage_type poly;
         std::unique_ptr<basic_sparse_set<Entity>> pool{};
-        void(* remove)(basic_sparse_set<Entity> &, basic_registry &, const Entity *, const Entity *){};
     };
 
     template<typename...>
@@ -115,11 +117,8 @@ class basic_registry {
         }
 
         if(auto &&pdata = pools[index]; !pdata.pool) {
-            pdata.info = type_id<Component>();
             pdata.pool.reset(new storage_type<Component>());
-            pdata.remove = +[](basic_sparse_set<Entity> &cpool, basic_registry &owner, const Entity *first, const Entity *last) {
-                static_cast<storage_type<Component> &>(cpool).remove(owner, first, last);
-            };
+            pdata.poly = std::ref(*static_cast<storage_type<Component> *>(pdata.pool.get()));
         }
 
         return static_cast<storage_type<Component> *>(pools[index].pool.get());
@@ -158,6 +157,8 @@ public:
     using version_type = typename traits_type::version_type;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
+    /*! @brief Poly storage type. */
+    using poly_storage = typename poly_storage_traits<Entity>::storage_type;
 
     /*! @brief Default constructor. */
     basic_registry() = default;
@@ -178,6 +179,15 @@ public:
         static_cast<void>(assure<Component>());
     }
 
+    /**
+     * @brief TODO
+     * @param info TODO
+     * @return TODO
+     */
+    poly_storage storage(const type_info info) {
+        return info.seq() < pools.size() ? pools[info.seq()].poly : poly_storage{};
+    }
+
     /**
      * @brief Returns the number of existing components of the given type.
      * @tparam Component Type of component of which to return the size.
@@ -765,7 +775,7 @@ public:
 
         for(auto pos = pools.size(); pos; --pos) {
             if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) {
-                pdata.remove(*pdata.pool, *this, std::begin(wrap), std::end(wrap));
+                pdata.poly->remove(*this, std::begin(wrap), std::end(wrap));
             }
         }
     }
@@ -912,8 +922,8 @@ public:
     void clear() {
         if constexpr(sizeof...(Component) == 0) {
             for(auto pos = pools.size(); pos; --pos) {
-                if(const auto &pdata = pools[pos-1]; pdata.pool) {
-                    pdata.remove(*pdata.pool, *this, pdata.pool->data(), pdata.pool->data() + pdata.pool->size());
+                if(auto &pdata = pools[pos-1]; pdata.pool) {
+                    pdata.poly->remove(*this, pdata.pool->rbegin(), pdata.pool->rend());
                 }
             }
 
@@ -1149,12 +1159,12 @@ public:
         std::vector<const basic_sparse_set<Entity> *> filter(std::distance(from, to));
 
         std::transform(first, last, component.begin(), [this](const auto ctype) {
-            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.info.hash() == ctype; });
+            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; });
             return it == pools.cend() ? nullptr : it->pool.get();
         });
 
         std::transform(from, to, filter.begin(), [this](const auto ctype) {
-            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.info.hash() == ctype; });
+            const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; });
             return it == pools.cend() ? nullptr : it->pool.get();
         });
 
@@ -1465,7 +1475,7 @@ public:
     void visit(entity_type entity, Func func) const {
         for(auto pos = pools.size(); pos; --pos) {
             if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) {
-                func(pdata.info);
+                func(pdata.poly->value_type());
             }
         }
     }
@@ -1494,7 +1504,7 @@ public:
     void visit(Func func) const {
         for(auto pos = pools.size(); pos; --pos) {
             if(const auto &pdata = pools[pos-1]; pdata.pool) {
-                func(pdata.info);
+                func(pdata.poly->value_type());
             }
         }
     }

+ 1 - 2
src/entt/entity/storage.hpp

@@ -787,8 +787,7 @@ private:
 
 
 /**
- * @brief Applies component-to-storage conversion and defines the resulting type
- * as the member typedef type.
+ * @brief Defines the component-to-storage conversion.
  *
  * Formally:
  *

+ 1 - 0
src/entt/entt.hpp

@@ -15,6 +15,7 @@
 #include "entity/helper.hpp"
 #include "entity/observer.hpp"
 #include "entity/organizer.hpp"
+#include "entity/poly_storage.hpp"
 #include "entity/registry.hpp"
 #include "entity/runtime_view.hpp"
 #include "entity/snapshot.hpp"

+ 23 - 27
src/entt/meta/meta.hpp

@@ -157,12 +157,7 @@ private:
 };
 
 
-/**
- * @brief Opaque wrapper for values of any type.
- *
- * This class uses a technique called small buffer optimization (SBO) to get rid
- * of memory allocations if possible. This should improve overall performance.
- */
+/*! @brief Opaque wrapper for values of any type. */
 class meta_any {
     enum class operation { DEREF, SEQ, ASSOC };
 
@@ -424,18 +419,6 @@ public:
         *this = meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
     }
 
-    /**
-     * @brief Aliasing constructor.
-     * @return A meta any that shares a reference to an unmanaged object.
-     */
-    [[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
-        meta_any other{};
-        other.node = node;
-        other.storage = storage.ref();
-        other.vtable = vtable;
-        return other;
-    }
-
     /**
      * @brief Returns a sequence container proxy.
      * @return A sequence container proxy for the underlying object.
@@ -496,6 +479,19 @@ public:
         swap(lhs.node, rhs.node);
     }
 
+    /**
+     * @brief Aliasing constructor.
+     * @param other A reference to an object that isn't necessarily initialized.
+     * @return A meta any that shares a reference to an unmanaged object.
+     */
+    [[nodiscard]] friend meta_any as_ref(const meta_any &other) ENTT_NOEXCEPT {
+        meta_any ref{};
+        ref.node = other.node;
+        ref.storage = as_ref(other.storage);
+        ref.vtable = other.vtable;
+        return ref;
+    }
+
 private:
     any storage;
     vtable_type *vtable;
@@ -535,7 +531,7 @@ struct meta_handle {
         : meta_handle{}
     {
         if constexpr(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, meta_any>) {
-            any = value.ref();
+            any = as_ref(value);
         } else {
             any = std::ref(value);
         }
@@ -1673,7 +1669,7 @@ public:
 
     /*! @brief Pre-increment operator. @return This iterator. */
     meta_iterator & operator++() ENTT_NOEXCEPT {
-        return next_fn(handle.ref()), *this;
+        return next_fn(as_ref(handle)), *this;
     }
 
     /*! @brief Post-increment operator. @return This iterator. */
@@ -1707,7 +1703,7 @@ public:
      * @return The element to which the meta pointer points.
      */
     [[nodiscard]] reference operator*() const {
-        return get_fn(handle.ref());
+        return get_fn(as_ref(handle));
     }
 
     /**
@@ -1838,7 +1834,7 @@ inline bool meta_sequence_container::clear() {
  * case of success) and a bool denoting whether the insertion took place.
  */
 inline std::pair<meta_sequence_container::iterator, bool> meta_sequence_container::insert(iterator it, meta_any value) {
-    return insert_fn(instance, it, value.ref());
+    return insert_fn(instance, it, as_ref(value));
 }
 
 
@@ -1931,7 +1927,7 @@ public:
 
     /*! @brief Pre-increment operator. @return This iterator. */
     meta_iterator & operator++() ENTT_NOEXCEPT {
-        return next_fn(handle.ref()), *this;
+        return next_fn(as_ref(handle)), *this;
     }
 
     /*! @brief Post-increment operator. @return This iterator. */
@@ -1965,7 +1961,7 @@ public:
      * @return The element to which the meta pointer points.
      */
     [[nodiscard]] reference operator*() const {
-        return { key_fn(handle.ref()), value_fn(handle.ref()) };
+        return { key_fn(as_ref(handle)), value_fn(as_ref(handle)) };
     }
 
     /**
@@ -2117,7 +2113,7 @@ inline bool meta_associative_container::clear() {
  * @return A bool denoting whether the insertion took place.
  */
 inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
-    return insert_fn(instance, key.ref(), value.ref());
+    return insert_fn(instance, as_ref(key), as_ref(value));
 }
 
 
@@ -2127,7 +2123,7 @@ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}
  * @return A bool denoting whether the removal took place.
  */
 inline bool meta_associative_container::erase(meta_any key) {
-    return erase_fn(instance, key.ref());
+    return erase_fn(instance, as_ref(key));
 }
 
 
@@ -2138,7 +2134,7 @@ inline bool meta_associative_container::erase(meta_any key) {
  * @return An iterator to the element with the given key, if any.
  */
 [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
-    return find_fn(instance, key.ref());
+    return find_fn(instance, as_ref(key));
 }
 
 

+ 113 - 109
src/entt/poly/poly.hpp

@@ -15,65 +15,6 @@
 namespace entt {
 
 
-/**
- * @brief Inline variable designed to contain the definition of a concept.
- * @tparam Concept A concept class template.
- * @tparam Type The type for which the definition is provided.
- */
-template<template<typename> class Concept, typename Type>
-inline constexpr auto poly_impl = value_list{};
-
-
-/*! @brief Static virtual table factory. */
-class poly_vtable {
-    template<typename Type, auto Candidate, typename Ret, typename... Args>
-    [[nodiscard]] static auto * vtable_entry(Ret(*)(Type &, Args...)) {
-        return +[](any &any, Args... args) -> Ret {
-            return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
-        };
-    }
-
-    template<typename Type, auto Candidate, typename Ret, typename... Args>
-    [[nodiscard]] static auto * vtable_entry(Ret(*)(const Type &, Args...)) {
-        return +[](const any &any, Args... args) -> Ret {
-            return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
-        };
-    }
-
-    template<typename Type, auto Candidate, typename Ret, typename... Args>
-    [[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...)) {
-        return +[](any &any, Args... args) -> Ret {
-            return std::invoke(Candidate, any_cast<Type &>(any), std::forward<Args>(args)...);
-        };
-    }
-
-    template<typename Type, auto Candidate, typename Ret, typename... Args>
-    [[nodiscard]] static auto * vtable_entry(Ret(Type:: *)(Args...) const) {
-        return +[](const any &any, Args... args) -> Ret {
-            return std::invoke(Candidate, any_cast<std::add_const_t<Type> &>(any), std::forward<Args>(args)...);
-        };
-    }
-
-    template<typename Type, auto... Impl>
-    [[nodiscard]] static auto * instance(value_list<Impl...>) {
-        static const auto vtable = std::make_tuple(vtable_entry<Type, Impl>(Impl)...);
-        return &vtable;
-    }
-
-public:
-    /**
-     * @brief Returns a static virtual table for a specific concept and type.
-     * @tparam Concept A concept class template.
-     * @tparam Type The type for which to generate the virtual table.
-     * @return A static virtual table for the given concept and type.
-     */
-    template<template<typename> class Concept, typename Type>
-    [[nodiscard]] static auto * instance() {
-        return instance<Type>(poly_impl<Concept, Type>);
-    }
-};
-
-
 /*! @brief Inspector class used to infer the type of the virtual table. */
 struct poly_inspector {
     /**
@@ -99,6 +40,77 @@ struct poly_inspector {
 };
 
 
+/**
+ * @brief Static virtual table factory.
+ * @tparam Concept Concept descriptor.
+ */
+template<typename Concept>
+class poly_vtable {
+    using inspector = typename Concept::template type<poly_inspector>;
+
+    template<typename Ret, typename... Args>
+    static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(any &, Args...);
+
+    template<typename Ret, typename... Args>
+    static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const any &, Args...);
+
+    template<typename Ret, typename... Args>
+    static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const any &, Args...);
+
+    template<typename Ret, typename... Args>
+    static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(any &, Args...);
+
+    template<typename Ret, typename... Args>
+    static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const any &, Args...);
+
+    template<auto... Candidate>
+    static auto make_vtable(value_list<Candidate...>)
+    -> std::tuple<decltype(vtable_entry(Candidate))...>;
+
+    template<typename... Func>
+    [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>)  {
+        if constexpr(sizeof...(Func) == 0) {
+            return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
+        } else if constexpr((std::is_function_v<Func> && ...)) {
+            return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector:: *>())...)){};
+        }
+    }
+
+    template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
+    static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) {
+        entry = +[](Any &any, Args... args) -> Ret {
+            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
+                return std::invoke(Candidate, std::forward<Args>(args)...);
+            } else {
+                return std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(any), std::forward<Args>(args)...);
+            }
+        };
+    }
+
+    template<typename Type, auto... Candidate, auto... Index>
+    [[nodiscard]] static auto fill_vtable(value_list<Candidate...>, std::index_sequence<Index...>) {
+        type impl{};
+        (fill_vtable_entry<Type, Candidate>(std::get<Index>(impl)), ...);
+        return impl;
+    }
+
+public:
+    /*! @brief Virtual table type. */
+    using type = decltype(make_vtable(Concept{}));
+
+    /**
+     * @brief Returns a static virtual table for a specific concept and type.
+     * @tparam Type The type for which to generate the virtual table.
+     * @return A static virtual table for the given concept and type.
+     */
+    template<typename Type>
+    [[nodiscard]] static const auto * instance() {
+        static const auto vtable = fill_vtable<Type>(typename Concept::template impl<Type>{}, std::make_index_sequence<std::tuple_size_v<type>>{});
+        return &vtable;
+    }
+};
+
+
 /**
  * @brief Poly base class used to inject functionalities into concepts.
  * @tparam Poly The outermost poly class.
@@ -141,7 +153,7 @@ template<auto Member, typename Poly, typename... Args>
 decltype(auto) poly_call(Poly &&self, Args &&... args) {
     return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
 }
-    
+
 
 /**
  * @brief Static polymorphism made simple and within everyone's reach.
@@ -150,45 +162,23 @@ decltype(auto) poly_call(Poly &&self, Args &&... args) {
  * cumbersome to obtain.<br/>
  * This class aims to make it simple and easy to use.
  *
- * Below is a minimal example of use:
- *
- * ```cpp
- * template<typename Base>
- * struct Drawable: Base {
- *     void draw() { entt::poly_call<0>(*this); }
- * };
- *
- * template<typename Type>
- * inline constexpr auto entt::poly_impl<Drawable, Type> = entt::value_list<&Type::draw>{};
- *
- * using drawable = entt::poly<Drawable>;
- *
- * struct circle { void draw() {} };
- * struct square { void draw() {} };
- *
- * int main() {
- *     drawable d{circle{}};
- *     d.draw();
+ * @note
+ * Both deduced and defined static virtual tables are supported.<br/>
+ * Moreover, the `poly` class template also works with unmanaged objects.
  *
- *     d = square{};
- *     d.draw();
- * }
- * ```
- *
- * The `poly` class template also supports aliasing for unmanaged objects.
- * Moreover, thanks to small buffer optimization, it limits the number of
- * allocations to a minimum where possible.
- *
- * @tparam Concept Concept class template.
+ * @tparam Concept Concept descriptor.
  */
-template<template<typename> class Concept>
-class poly: public Concept<poly_base<poly<Concept>>> {
+template<typename Concept>
+class poly: private Concept::template type<poly_base<poly<Concept>>> {
     /*! @brief A poly base is allowed to snoop into a poly object. */
     friend struct poly_base<poly<Concept>>;
 
-    using vtable_t = std::remove_pointer_t<decltype(poly_vtable::instance<Concept, Concept<poly_inspector>>())>;
+    using vtable_type = typename poly_vtable<Concept>::type;
 
 public:
+    /*! @brief Concept interface type. */
+    using interface = typename Concept::template type<poly_base<poly<Concept>>>;
+
     /*! @brief Default constructor. */
     poly() ENTT_NOEXCEPT
         : storage{},
@@ -204,7 +194,7 @@ public:
     template<typename Type, typename... Args>
     explicit poly(std::in_place_type_t<Type>, Args &&... args)
         : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
-          vtable{poly_vtable::instance<Concept, Type>()}
+          vtable{poly_vtable<Concept>::template instance<Type>()}
     {}
 
     /**
@@ -215,7 +205,7 @@ public:
     template<typename Type>
     poly(std::reference_wrapper<Type> value)
         : storage{value},
-          vtable{poly_vtable::instance<Concept, Type>()}
+          vtable{poly_vtable<Concept>::template instance<Type>()}
     {}
 
     /**
@@ -224,7 +214,7 @@ public:
      * @param value An instance of an object to use to initialize the poly.
      */
     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, poly>>>
-    poly(Type &&value)
+    poly(Type &&value) ENTT_NOEXCEPT
         : poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
     {}
 
@@ -238,7 +228,7 @@ public:
      * @brief Move constructor.
      * @param other The instance to move from.
      */
-    poly(poly &&other)
+    poly(poly &&other) ENTT_NOEXCEPT
         : poly{}
     {
         swap(*this, other);
@@ -284,18 +274,7 @@ public:
     template<typename Type, typename... Args>
     void emplace(Args &&... args) {
         storage.emplace<Type>(std::forward<Args>(args)...);
-        vtable = poly_vtable::instance<Concept, Type>();
-    }
-
-    /**
-     * @brief Aliasing constructor.
-     * @return A poly that shares a reference to an unmanaged object.
-     */
-    [[nodiscard]] poly ref() const ENTT_NOEXCEPT {
-        poly other{};
-        other.storage = storage.ref();
-        other.vtable = vtable;
-        return other;
+        vtable = poly_vtable<Concept>::template instance<Type>();
     }
 
     /**
@@ -306,6 +285,19 @@ public:
         return !(vtable == nullptr);
     }
 
+    /**
+     * @brief Returns a pointer to the underlying concept.
+     * @return A pointer to the underlying concept.
+     */
+    [[nodiscard]] interface * operator->() ENTT_NOEXCEPT {
+        return this;
+    }
+
+    /*! @copydoc operator-> */
+    [[nodiscard]] const interface * operator->() const ENTT_NOEXCEPT {
+        return this;
+    }
+
     /**
      * @brief Swaps two poly objects.
      * @param lhs A valid poly object.
@@ -317,9 +309,21 @@ public:
         swap(lhs.vtable, rhs.vtable);
     }
 
+    /**
+     * @brief Aliasing constructor.
+     * @param other A reference to an object that isn't necessarily initialized.
+     * @return A poly that shares a reference to an unmanaged object.
+     */
+    [[nodiscard]] friend poly as_ref(const poly &other) ENTT_NOEXCEPT {
+        poly ref;
+        ref.storage = as_ref(other.storage);
+        ref.vtable = other.vtable;
+        return ref;
+    }
+
 private:
     any storage;
-    const vtable_t *vtable;
+    const vtable_type *vtable;
 };
 
 

+ 3 - 1
test/CMakeLists.txt

@@ -169,6 +169,7 @@ SETUP_BASIC_TEST(group entt/entity/group.cpp)
 SETUP_BASIC_TEST(handle entt/entity/handle.cpp)
 SETUP_BASIC_TEST(helper entt/entity/helper.cpp)
 SETUP_BASIC_TEST(observer entt/entity/observer.cpp)
+SETUP_BASIC_TEST(poly_storage entt/entity/poly_storage.cpp)
 SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp)
 SETUP_BASIC_TEST(registry entt/entity/registry.cpp)
 SETUP_BASIC_TEST(registry_no_eto entt/entity/registry_no_eto.cpp ENTT_NO_ETO)
@@ -199,7 +200,8 @@ SETUP_BASIC_TEST(meta_type entt/meta/meta_type.cpp)
 
 # Test poly
 
-SETUP_BASIC_TEST(poly entt/poly/poly.cpp)
+SETUP_BASIC_TEST(poly_deduced entt/poly/poly_deduced.cpp)
+SETUP_BASIC_TEST(poly_defined entt/poly/poly_defined.cpp)
 
 # Test process
 

+ 4 - 4
test/entt/core/any.cpp

@@ -54,7 +54,7 @@ TEST(Any, SBOInPlaceTypeConstruction) {
     ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
     ASSERT_EQ(entt::any_cast<int>(any), 42);
 
-    auto other = any.ref();
+    auto other = as_ref(any);
 
     ASSERT_TRUE(other);
     ASSERT_EQ(other.type(), entt::type_id<int>());
@@ -72,7 +72,7 @@ TEST(Any, SBOAsRefConstruction) {
     ASSERT_EQ(entt::any_cast<int>(any), 42);
     ASSERT_EQ(any.data(), &value);
 
-    auto other = any.ref();
+    auto other = as_ref(any);
 
     ASSERT_TRUE(other);
     ASSERT_EQ(other.type(), entt::type_id<int>());
@@ -151,7 +151,7 @@ TEST(Any, NoSBOInPlaceTypeConstruction) {
     ASSERT_EQ(entt::any_cast<double>(&any), nullptr);
     ASSERT_EQ(entt::any_cast<fat>(any), instance);
 
-    auto other = any.ref();
+    auto other = as_ref(any);
 
     ASSERT_TRUE(other);
     ASSERT_EQ(other.type(), entt::type_id<fat>());
@@ -169,7 +169,7 @@ TEST(Any, NoSBOAsRefConstruction) {
     ASSERT_EQ(entt::any_cast<fat>(any), instance);
     ASSERT_EQ(any.data(), &instance);
 
-    auto other = any.ref();
+    auto other = as_ref(any);
 
     ASSERT_TRUE(other);
     ASSERT_EQ(other.type(), entt::type_id<fat>());

+ 135 - 0
test/entt/entity/poly_storage.cpp

@@ -0,0 +1,135 @@
+#include <cstddef>
+#include <gtest/gtest.h>
+#include <entt/core/utility.hpp>
+#include <entt/entity/poly_storage.hpp>
+#include <entt/entity/registry.hpp>
+
+template<typename... Type>
+entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
+
+template<typename Entity>
+struct PolyStorage: entt::type_list_cat_t<
+    decltype(as_type_list(std::declval<entt::Storage<Entity>>())),
+    entt::type_list<
+        void(entt::basic_registry<Entity> &, const Entity, const void *),
+        const void *(const Entity) const,
+        const Entity *() const,
+        const void *() const,
+        std::size_t() const,
+        void(entt::basic_registry<Entity> &, const Entity *, const void *, const std::size_t)
+    >
+> {
+    using entity_type = Entity;
+    using size_type = std::size_t;
+
+    template<typename Base>
+    struct type: entt::Storage<Entity>::template type<Base> {
+        static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<entt::Storage<Entity>>::type>;
+
+        void emplace(entt::basic_registry<entity_type> &owner, const entity_type entity, const void *instance) {
+            entt::poly_call<base + 0>(*this, owner, entity, instance);
+        }
+
+        const void * get(const entity_type entity) const {
+            return entt::poly_call<base + 1>(*this, entity);
+        }
+
+        const entity_type * data() const {
+            return entt::poly_call<base + 2>(*this);
+        }
+
+        const void * raw() const {
+            return entt::poly_call<base + 3>(*this);
+        }
+
+        size_type size() const {
+            return entt::poly_call<base + 4>(*this);
+        }
+
+        void insert(entt::basic_registry<Entity> &owner, const Entity *entity, const void *instance, const std::size_t length) {
+            entt::poly_call<base + 5>(*this, owner, entity, instance, length);
+        }
+    };
+
+    template<typename Type>
+    struct members {
+        static void emplace(Type &self, entt::basic_registry<entity_type> &owner, const entity_type entity, const void *instance) {
+            self.emplace(owner, entity, *static_cast<const typename Type::value_type *>(instance));
+        }
+
+        static const typename Type::value_type * get(const Type &self, const entity_type entity) {
+            return &self.get(entity);
+        }
+
+        static void insert(Type &self, entt::basic_registry<entity_type> &owner, const entity_type *entity, const void *instance, const size_type length) {
+            const auto *value = static_cast<const typename Type::value_type *>(instance);
+            self.insert(owner, entity, entity + length, value, value + length);
+        }
+    };
+
+    template<typename Type>
+    using impl = entt::value_list_cat_t<
+        typename entt::Storage<Entity>::template impl<Type>,
+        entt::value_list<
+            &members<Type>::emplace,
+            &members<Type>::get,
+            &Type::data,
+            entt::overload<const typename Type::value_type *() const ENTT_NOEXCEPT>(&Type::raw),
+            &Type::size,
+            &members<Type>::insert
+        >
+    >;
+};
+
+template<typename Entity>
+struct entt::poly_storage_traits<Entity> {
+    using storage_type = entt::poly<PolyStorage<Entity>>;
+};
+
+TEST(PolyStorage, CopyEntity) {
+    entt::registry registry;
+    const auto entity = registry.create();
+    const auto other = registry.create();
+
+    registry.emplace<int>(entity, 42);
+    registry.emplace<char>(entity, 'c');
+
+    ASSERT_TRUE((registry.has<int, char>(entity)));
+    ASSERT_FALSE((registry.any<int, char>(other)));
+
+    registry.visit(entity, [&](const auto info) {
+        auto storage = registry.storage(info);
+        storage->emplace(registry, other, storage->get(entity));
+    });
+
+    ASSERT_TRUE((registry.has<int, char>(entity)));
+    ASSERT_TRUE((registry.has<int, char>(other)));
+
+    ASSERT_EQ(registry.get<int>(entity), registry.get<int>(other));
+    ASSERT_EQ(registry.get<char>(entity), registry.get<char>(other));
+}
+
+TEST(PolyStorage, CopyRegistry) {
+    entt::registry registry;
+    entt::registry other;
+    entt::entity entities[10u];
+
+    registry.create(std::begin(entities), std::end(entities));
+    registry.insert<int>(std::begin(entities), std::end(entities), 42);
+    registry.insert<char>(std::begin(entities), std::end(entities), 'c');
+
+    other.prepare<int>();
+    other.prepare<char>();
+
+    ASSERT_EQ(registry.size(), 10u);
+    ASSERT_EQ(other.size(), 0u);
+
+    other.assign(registry.data(), registry.data() + registry.size(), registry.destroyed());
+
+    registry.visit([&](const auto info) {
+        auto storage = registry.storage(info);
+        other.storage(info)->insert(other, storage->data(), storage->raw(), storage->size());
+    });
+
+    ASSERT_EQ(registry.size(), other.size());
+}

+ 1 - 1
test/entt/meta/meta_func.cpp

@@ -337,7 +337,7 @@ TEST_F(MetaFunc, ByReference) {
     int value = 4;
 
     ASSERT_EQ(func.invoke({}, std::ref(value)).cast<int>(), 8);
-    ASSERT_EQ(func.invoke({}, any.ref()).cast<int>(), 6);
+    ASSERT_EQ(func.invoke({}, as_ref(any)).cast<int>(), 6);
     ASSERT_EQ(any.cast<int>(), 6);
     ASSERT_EQ(value, 8);
 }

+ 0 - 148
test/entt/poly/poly.cpp

@@ -1,148 +0,0 @@
-#include <functional>
-#include <type_traits>
-#include <gtest/gtest.h>
-#include <entt/poly/poly.hpp>
-
-template<typename Base>
-struct concept: Base {
-    void incr() { entt::poly_call<0>(*this); }
-    void set(int v) { entt::poly_call<1>(*this, v); }
-    int get() const { return entt::poly_call<2>(*this); }
-    void decr() { entt::poly_call<3>(*this); }
-    int mul(int v) { return entt::poly_call<4>(*this, v); }
-};
-
-template<typename Type>
-void decr(Type &self) {
-    self.set(self.get()-1);
-}
-
-template<typename Type>
-int mul(const Type &self, int v) {
-    return v * self.get();
-}
-
-template<typename Type>
-inline constexpr auto entt::poly_impl<concept, Type> =
-    entt::value_list<
-        &Type::incr,
-        &Type::set,
-        &Type::get,
-        &decr<Type>,
-        &mul<Type>
-    >{};
-
-struct impl {
-    void incr() { ++value; }
-    void set(int v) { value = v; }
-    int get() const { return value; }
-    int value{};
-};
-
-TEST(Poly, Functionalities) {
-    impl instance{};
-
-    entt::poly<concept> empty{};
-    entt::poly<concept> in_place{std::in_place_type<impl>, 3};
-    entt::poly<concept> alias{std::ref(instance)};
-    entt::poly<concept> value{impl{}};
-
-    ASSERT_FALSE(empty);
-    ASSERT_TRUE(in_place);
-    ASSERT_TRUE(alias);
-    ASSERT_TRUE(value);
-
-    ASSERT_EQ(empty.type(), entt::type_info{});
-    ASSERT_EQ(in_place.type(), entt::type_id<impl>());
-    ASSERT_EQ(alias.type(), entt::type_id<impl>());
-    ASSERT_EQ(value.type(), entt::type_id<impl>());
-
-    ASSERT_EQ(alias.data(), &instance);
-    ASSERT_EQ(std::as_const(alias).data(), &instance);
-
-    empty = impl{};
-
-    ASSERT_TRUE(empty);
-    ASSERT_NE(empty.data(), nullptr);
-    ASSERT_NE(std::as_const(empty).data(), nullptr);
-    ASSERT_EQ(empty.type(), entt::type_id<impl>());
-    ASSERT_EQ(empty.get(), 0);
-
-    empty.emplace<impl>(3);
-
-    ASSERT_TRUE(empty);
-    ASSERT_EQ(empty.get(), 3);
-
-    entt::poly<concept> ref = in_place.ref();
-
-    ASSERT_TRUE(ref);
-    ASSERT_NE(ref.data(), nullptr);
-    ASSERT_EQ(ref.data(), in_place.data());
-    ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
-    ASSERT_EQ(ref.type(), entt::type_id<impl>());
-    ASSERT_EQ(ref.get(), 3);
-
-    entt::poly<concept> null{};
-    std::swap(empty, null);
-
-    ASSERT_FALSE(empty);
-
-    entt::poly<concept> copy = in_place;
-
-    ASSERT_TRUE(copy);
-    ASSERT_EQ(copy.get(), 3);
-
-    entt::poly<concept> move = std::move(copy);
-
-    ASSERT_TRUE(move);
-    ASSERT_FALSE(copy);
-    ASSERT_EQ(move.get(), 3);
-}
-
-TEST(Poly, Owned) {
-    entt::poly<concept> poly{impl{}};
-    auto *ptr = static_cast<impl *>(poly.data());
-    
-    ASSERT_TRUE(poly);
-    ASSERT_NE(poly.data(), nullptr);
-    ASSERT_NE(std::as_const(poly).data(), nullptr);
-    ASSERT_EQ(ptr->value, 0);
-    ASSERT_EQ(poly.get(), 0);
-    
-    poly.set(1);
-    poly.incr();
-    
-    ASSERT_EQ(ptr->value, 2);
-    ASSERT_EQ(poly.get(), 2);
-    ASSERT_EQ(poly.mul(3), 6);
-
-    poly.decr();
-
-    ASSERT_EQ(ptr->value, 1);
-    ASSERT_EQ(poly.get(), 1);
-    ASSERT_EQ(poly.mul(3), 3);
-}
-
-TEST(Poly, Alias) {
-    impl instance{};
-    entt::poly<concept> poly{std::ref(instance)};
-    
-    ASSERT_TRUE(poly);
-    ASSERT_NE(poly.data(), nullptr);
-    ASSERT_NE(std::as_const(poly).data(), nullptr);
-    ASSERT_EQ(instance.value, 0);
-    ASSERT_EQ(poly.get(), 0);
-    
-    poly.set(1);
-    poly.incr();
-    
-    ASSERT_EQ(instance.value, 2);
-    ASSERT_EQ(poly.get(), 2);
-    ASSERT_EQ(poly.mul(3), 6);
-
-    poly.decr();
-
-    ASSERT_EQ(instance.value, 1);
-    ASSERT_EQ(poly.get(), 1);
-    ASSERT_EQ(poly.mul(3), 3);
-}

+ 148 - 0
test/entt/poly/poly_deduced.cpp

@@ -0,0 +1,148 @@
+#include <functional>
+#include <type_traits>
+#include <gtest/gtest.h>
+#include <entt/core/type_traits.hpp>
+#include <entt/poly/poly.hpp>
+
+struct Deduced: entt::type_list<> {
+    template<typename Base>
+    struct type: Base {
+        void incr() { entt::poly_call<0>(*this); }
+        void set(int v) { entt::poly_call<1>(*this, v); }
+        int get() const { return entt::poly_call<2>(*this); }
+        void decr() { entt::poly_call<3>(*this); }
+        int mul(int v) { return entt::poly_call<4>(*this, v); }
+    };
+
+    template<typename Type>
+    struct members {
+        static void decr(Type &self) { self.set(self.get()-1); }
+        static double mul(Type &self, double v) { return v * self.get(); }
+    };
+
+    template<typename Type>
+    using impl = entt::value_list<
+        &Type::incr,
+        &Type::set,
+        &Type::get,
+        &members<Type>::decr,
+        &members<Type>::mul
+    >;
+};
+
+struct impl {
+    void incr() { ++value; }
+    void set(int v) { value = v; }
+    int get() const { return value; }
+    void decrement() { --value; }
+    double multiply(double v) { return v * value; }
+    int value{};
+};
+
+TEST(PolyDeduced, Functionalities) {
+    impl instance{};
+
+    entt::poly<Deduced> empty{};
+    entt::poly<Deduced> in_place{std::in_place_type<impl>, 3};
+    entt::poly<Deduced> alias{std::ref(instance)};
+    entt::poly<Deduced> value{impl{}};
+
+    ASSERT_FALSE(empty);
+    ASSERT_TRUE(in_place);
+    ASSERT_TRUE(alias);
+    ASSERT_TRUE(value);
+
+    ASSERT_EQ(empty.type(), entt::type_info{});
+    ASSERT_EQ(in_place.type(), entt::type_id<impl>());
+    ASSERT_EQ(alias.type(), entt::type_id<impl>());
+    ASSERT_EQ(value.type(), entt::type_id<impl>());
+
+    ASSERT_EQ(alias.data(), &instance);
+    ASSERT_EQ(std::as_const(alias).data(), &instance);
+
+    empty = impl{};
+
+    ASSERT_TRUE(empty);
+    ASSERT_NE(empty.data(), nullptr);
+    ASSERT_NE(std::as_const(empty).data(), nullptr);
+    ASSERT_EQ(empty.type(), entt::type_id<impl>());
+    ASSERT_EQ(empty->get(), 0);
+
+    empty.template emplace<impl>(3);
+
+    ASSERT_TRUE(empty);
+    ASSERT_EQ(empty->get(), 3);
+
+    entt::poly<Deduced> ref = as_ref(in_place);
+
+    ASSERT_TRUE(ref);
+    ASSERT_NE(ref.data(), nullptr);
+    ASSERT_EQ(ref.data(), in_place.data());
+    ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
+    ASSERT_EQ(ref.type(), entt::type_id<impl>());
+    ASSERT_EQ(ref->get(), 3);
+
+    entt::poly<Deduced> null{};
+    std::swap(empty, null);
+
+    ASSERT_FALSE(empty);
+
+    entt::poly<Deduced> copy = in_place;
+
+    ASSERT_TRUE(copy);
+    ASSERT_EQ(copy->get(), 3);
+
+    entt::poly<Deduced> move = std::move(copy);
+
+    ASSERT_TRUE(move);
+    ASSERT_FALSE(copy);
+    ASSERT_EQ(move->get(), 3);
+}
+
+TEST(PolyDeduced, Owned) {
+    entt::poly<Deduced> poly{impl{}};
+    auto *ptr = static_cast<impl *>(poly.data());
+
+    ASSERT_TRUE(poly);
+    ASSERT_NE(poly.data(), nullptr);
+    ASSERT_NE(std::as_const(poly).data(), nullptr);
+    ASSERT_EQ(ptr->value, 0);
+    ASSERT_EQ(poly->get(), 0);
+
+    poly->set(1);
+    poly->incr();
+
+    ASSERT_EQ(ptr->value, 2);
+    ASSERT_EQ(poly->get(), 2);
+    ASSERT_EQ(poly->mul(3), 6);
+
+    poly->decr();
+
+    ASSERT_EQ(ptr->value, 1);
+    ASSERT_EQ(poly->get(), 1);
+    ASSERT_EQ(poly->mul(3), 3);
+}
+
+TEST(PolyDeduced, Alias) {
+    impl instance{};
+    entt::poly<Deduced> poly{std::ref(instance)};
+
+    ASSERT_TRUE(poly);
+    ASSERT_NE(poly.data(), nullptr);
+    ASSERT_NE(std::as_const(poly).data(), nullptr);
+    ASSERT_EQ(instance.value, 0);
+    ASSERT_EQ(poly->get(), 0);
+
+    poly->set(1);
+    poly->incr();
+
+    ASSERT_EQ(instance.value, 2);
+    ASSERT_EQ(poly->get(), 2);
+    ASSERT_EQ(poly->mul(3), 6);
+
+    poly->decr();
+
+    ASSERT_EQ(instance.value, 1);
+    ASSERT_EQ(poly->get(), 1);
+    ASSERT_EQ(poly->mul(3), 3);
+}

+ 154 - 0
test/entt/poly/poly_defined.cpp

@@ -0,0 +1,154 @@
+#include <functional>
+#include <type_traits>
+#include <gtest/gtest.h>
+#include <entt/core/type_traits.hpp>
+#include <entt/poly/poly.hpp>
+
+struct Defined: entt::type_list<
+    void(),
+    void(int),
+    int() const,
+    void(),
+    int(int)
+> {
+    template<typename Base>
+    struct type: Base {
+        void incr() { entt::poly_call<0>(*this); }
+        void set(int v) { entt::poly_call<1>(*this, v); }
+        int get() const { return entt::poly_call<2>(*this); }
+        void decr() { entt::poly_call<3>(*this); }
+        int mul(int v) { return entt::poly_call<4>(*this, v); }
+    };
+
+    template<typename Type>
+    struct members {
+        static void decr(Type &self) { self.decrement(); }
+        static double mul(Type &self, double v) { return self.multiply(v); }
+    };
+
+    template<typename Type>
+    using impl = entt::value_list<
+        &Type::incr,
+        &Type::set,
+        &Type::get,
+        &members<Type>::decr,
+        &members<Type>::mul
+    >;
+};
+
+struct impl {
+    void incr() { ++value; }
+    void set(int v) { value = v; }
+    int get() const { return value; }
+    void decrement() { --value; }
+    double multiply(double v) { return v * value; }
+    int value{};
+};
+
+TEST(PolyDefined, Functionalities) {
+    impl instance{};
+
+    entt::poly<Defined> empty{};
+    entt::poly<Defined> in_place{std::in_place_type<impl>, 3};
+    entt::poly<Defined> alias{std::ref(instance)};
+    entt::poly<Defined> value{impl{}};
+
+    ASSERT_FALSE(empty);
+    ASSERT_TRUE(in_place);
+    ASSERT_TRUE(alias);
+    ASSERT_TRUE(value);
+
+    ASSERT_EQ(empty.type(), entt::type_info{});
+    ASSERT_EQ(in_place.type(), entt::type_id<impl>());
+    ASSERT_EQ(alias.type(), entt::type_id<impl>());
+    ASSERT_EQ(value.type(), entt::type_id<impl>());
+
+    ASSERT_EQ(alias.data(), &instance);
+    ASSERT_EQ(std::as_const(alias).data(), &instance);
+
+    empty = impl{};
+
+    ASSERT_TRUE(empty);
+    ASSERT_NE(empty.data(), nullptr);
+    ASSERT_NE(std::as_const(empty).data(), nullptr);
+    ASSERT_EQ(empty.type(), entt::type_id<impl>());
+    ASSERT_EQ(empty->get(), 0);
+
+    empty.template emplace<impl>(3);
+
+    ASSERT_TRUE(empty);
+    ASSERT_EQ(empty->get(), 3);
+
+    entt::poly<Defined> ref = as_ref(in_place);
+
+    ASSERT_TRUE(ref);
+    ASSERT_NE(ref.data(), nullptr);
+    ASSERT_EQ(ref.data(), in_place.data());
+    ASSERT_EQ(std::as_const(ref).data(), std::as_const(in_place).data());
+    ASSERT_EQ(ref.type(), entt::type_id<impl>());
+    ASSERT_EQ(ref->get(), 3);
+
+    entt::poly<Defined> null{};
+    std::swap(empty, null);
+
+    ASSERT_FALSE(empty);
+
+    entt::poly<Defined> copy = in_place;
+
+    ASSERT_TRUE(copy);
+    ASSERT_EQ(copy->get(), 3);
+
+    entt::poly<Defined> move = std::move(copy);
+
+    ASSERT_TRUE(move);
+    ASSERT_FALSE(copy);
+    ASSERT_EQ(move->get(), 3);
+}
+
+TEST(PolyDefined, Owned) {
+    entt::poly<Defined> poly{impl{}};
+    auto *ptr = static_cast<impl *>(poly.data());
+
+    ASSERT_TRUE(poly);
+    ASSERT_NE(poly.data(), nullptr);
+    ASSERT_NE(std::as_const(poly).data(), nullptr);
+    ASSERT_EQ(ptr->value, 0);
+    ASSERT_EQ(poly->get(), 0);
+
+    poly->set(1);
+    poly->incr();
+
+    ASSERT_EQ(ptr->value, 2);
+    ASSERT_EQ(poly->get(), 2);
+    ASSERT_EQ(poly->mul(3), 6);
+
+    poly->decr();
+
+    ASSERT_EQ(ptr->value, 1);
+    ASSERT_EQ(poly->get(), 1);
+    ASSERT_EQ(poly->mul(3), 3);
+}
+
+TEST(PolyDefined, Alias) {
+    impl instance{};
+    entt::poly<Defined> poly{std::ref(instance)};
+
+    ASSERT_TRUE(poly);
+    ASSERT_NE(poly.data(), nullptr);
+    ASSERT_NE(std::as_const(poly).data(), nullptr);
+    ASSERT_EQ(instance.value, 0);
+    ASSERT_EQ(poly->get(), 0);
+
+    poly->set(1);
+    poly->incr();
+
+    ASSERT_EQ(instance.value, 2);
+    ASSERT_EQ(poly->get(), 2);
+    ASSERT_EQ(poly->mul(3), 6);
+
+    poly->decr();
+
+    ASSERT_EQ(instance.value, 1);
+    ASSERT_EQ(poly->get(), 1);
+    ASSERT_EQ(poly->mul(3), 3);
+}