diff --git a/docs/md/core.md b/docs/md/core.md
index 7250a7752..8d18e2ee3 100644
--- a/docs/md/core.md
+++ b/docs/md/core.md
@@ -14,6 +14,9 @@
* [Monostate](#monostate)
* [Type info](#type-info)
* [Almost unique identifiers](#almost-unique-identifiers)
+* [Traits](#traits)
+ * [Member class type](#member-class-type)
+ * [Tags](#tags)
@@ -256,3 +259,38 @@ many others. As already mentioned above, since users have full control over
their types, this problem is in any case easy to solve and should not worry too
much.
In all likelihood, it will never happen to run into a conflict anyway.
+
+# Traits
+
+This section contains a handful of utilities and traits not present in the
+standard template library but which can be useful in everyday life.
+
+## Member class type
+
+The `auto` template parameter introduced with C++17 made it possible to simplify
+many class templates and template functions but also made the class type opaque
+when members are passed as template arguments.
+The purpose of this utility is to extract the class type in a few lines of code:
+
+```cpp
+template
+using clazz = entt::member_class_t;
+```
+
+## Tags
+
+Since in `EnTT` the type identified by `ENTT_ID_TYPE` is very important and
+widely used, there is a more user-friendly shortcut for the creation of integral
+constants based on it.
+This shortcut is the alias template `entt::tag`.
+
+If used in combination with hashed strings, it helps to use human-readable names
+where types would be required otherwise. As an example:
+
+```cpp
+registry.assign>(entity);
+```
+
+However, this isn't the only permitted use. Literally any value convertible to
+`ENTT_ID_TYPE` is a good candidate, such as the named constants of an unscoped
+enum.
diff --git a/docs/md/entity.md b/docs/md/entity.md
index ff8130be1..58a8f6ef7 100644
--- a/docs/md/entity.md
+++ b/docs/md/entity.md
@@ -20,7 +20,6 @@
* [Null entity](#null-entity)
* [Stamp](#stamp)
* [Dependencies](#dependencies)
- * [Tags](#tags)
* [Actor](#actor)
* [Context variables](#context-variables)
* [Meet the runtime](#meet-the-runtime)
@@ -626,20 +625,6 @@ There are many other types of dependencies. In general, most of the functions
that accept an entity as the first argument are good candidates for this
purpose.
-### Tags
-
-There's nothing magical about the way tags can be assigned to entities while
-avoiding a performance hit at runtime. Nonetheless, the syntax can be annoying
-and that's why a more user-friendly shortcut is provided to do it.
-This shortcut is the alias template `entt::tag`.
-
-If used in combination with hashed strings, it helps to use tags where types
-would be required otherwise. As an example:
-
-```cpp
-registry.assign>(entity);
-```
-
### Actor
The `actor` class is designed for those who don't feel immediately comfortable
diff --git a/src/entt/core/type_traits.hpp b/src/entt/core/type_traits.hpp
index 9561c7b5a..06acf687f 100644
--- a/src/entt/core/type_traits.hpp
+++ b/src/entt/core/type_traits.hpp
@@ -201,6 +201,14 @@ template
using member_class_t = typename member_class::type;
+/**
+ * @brief Alias template to ease the creation of named values.
+ * @tparam Value A constant value at least convertible to `ENTT_ID_TYPE`.
+ */
+template
+using tag = std::integral_constant;
+
+
}
diff --git a/src/entt/entity/helper.hpp b/src/entt/entity/helper.hpp
index 3c4bbc667..38c4d39fb 100644
--- a/src/entt/entity/helper.hpp
+++ b/src/entt/entity/helper.hpp
@@ -142,29 +142,6 @@ template
as_group(const basic_registry &) ENTT_NOEXCEPT -> as_group;
-/**
- * @brief Alias template to ease the assignment of tags to entities.
- *
- * If used in combination with hashed strings, it simplifies the assignment of
- * tags to entities and the use of tags in general where a type would be
- * required otherwise.
- * As an example and where the user defined literal for hashed strings hasn't
- * been changed:
- * @code{.cpp}
- * entt::registry registry;
- * registry.assign>(entity);
- * @endcode
- *
- * @note
- * Tags are empty components and therefore candidates for the empty component
- * optimization.
- *
- * @tparam Value The numeric representation of an instance of hashed string.
- */
-template
-using tag = std::integral_constant;
-
-
}
diff --git a/test/entt/core/type_traits.cpp b/test/entt/core/type_traits.cpp
index 42252611d..c3a200bef 100644
--- a/test/entt/core/type_traits.cpp
+++ b/test/entt/core/type_traits.cpp
@@ -1,4 +1,6 @@
#include
+#include
+#include
#include
TEST(Choice, Functionalities) {
@@ -34,3 +36,8 @@ TEST(MemberClass, Functionalities) {
ASSERT_TRUE((std::is_same_v>));
ASSERT_TRUE((std::is_same_v>));
}
+
+TEST(Tag, Functionalities) {
+ ASSERT_EQ(entt::tag<"foobar"_hs>::value, entt::hashed_string::value("foobar"));
+ ASSERT_TRUE((std::is_same_v::value_type, ENTT_ID_TYPE>));
+}
diff --git a/test/entt/entity/helper.cpp b/test/entt/entity/helper.cpp
index 7a7504391..e1887a6fc 100644
--- a/test/entt/entity/helper.cpp
+++ b/test/entt/entity/helper.cpp
@@ -22,28 +22,3 @@ TEST(Helper, AsGroup) {
([](entt::group, entt::get_t, double>) {})(entt::as_group{registry});
([](entt::group, entt::get_t, const double>) {})(entt::as_group{registry});
}
-
-TEST(Helper, Tag) {
- entt::registry registry;
- const auto entity = registry.create();
- registry.assign>(entity);
- registry.assign(entity, 42);
- int counter{};
-
- ASSERT_FALSE(registry.has>(entity));
- ASSERT_TRUE(registry.has>(entity));
-
- for(auto entt: registry.view>()) {
- (void)entt;
- ++counter;
- }
-
- ASSERT_NE(counter, 0);
-
- for(auto entt: registry.view>()) {
- (void)entt;
- --counter;
- }
-
- ASSERT_EQ(counter, 0);
-}