compressed_pair.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
  2. #define ENTT_CORE_COMPRESSED_PAIR_HPP
  3. #include <cstddef>
  4. #include <tuple>
  5. #include <type_traits>
  6. #include <utility>
  7. #include "type_traits.hpp"
  8. namespace entt {
  9. /**
  10. * @cond TURN_OFF_DOXYGEN
  11. * Internal details not to be documented.
  12. */
  13. namespace internal {
  14. template<typename Type, std::size_t, typename = void>
  15. struct compressed_pair_element {
  16. using reference = Type &;
  17. using const_reference = const Type &;
  18. template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
  19. constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
  20. : value{} {}
  21. template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
  22. constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
  23. : value{std::forward<Arg>(arg)} {}
  24. template<typename... Args, std::size_t... Index>
  25. constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
  26. : value{std::forward<Args>(std::get<Index>(args))...} {}
  27. [[nodiscard]] constexpr reference get() noexcept {
  28. return value;
  29. }
  30. [[nodiscard]] constexpr const_reference get() const noexcept {
  31. return value;
  32. }
  33. private:
  34. Type value;
  35. };
  36. template<typename Type, std::size_t Tag>
  37. struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
  38. using reference = Type &;
  39. using const_reference = const Type &;
  40. using base_type = Type;
  41. template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
  42. constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
  43. : base_type{} {}
  44. template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
  45. constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
  46. : base_type{std::forward<Arg>(arg)} {}
  47. template<typename... Args, std::size_t... Index>
  48. constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
  49. : base_type{std::forward<Args>(std::get<Index>(args))...} {}
  50. [[nodiscard]] constexpr reference get() noexcept {
  51. return *this;
  52. }
  53. [[nodiscard]] constexpr const_reference get() const noexcept {
  54. return *this;
  55. }
  56. };
  57. } // namespace internal
  58. /**
  59. * Internal details not to be documented.
  60. * @endcond
  61. */
  62. /**
  63. * @brief A compressed pair.
  64. *
  65. * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
  66. * reduce its final size to a minimum.
  67. *
  68. * @tparam First The type of the first element that the pair stores.
  69. * @tparam Second The type of the second element that the pair stores.
  70. */
  71. template<typename First, typename Second>
  72. class compressed_pair final
  73. : internal::compressed_pair_element<First, 0u>,
  74. internal::compressed_pair_element<Second, 1u> {
  75. using first_base = internal::compressed_pair_element<First, 0u>;
  76. using second_base = internal::compressed_pair_element<Second, 1u>;
  77. public:
  78. /*! @brief The type of the first element that the pair stores. */
  79. using first_type = First;
  80. /*! @brief The type of the second element that the pair stores. */
  81. using second_type = Second;
  82. /**
  83. * @brief Default constructor, conditionally enabled.
  84. *
  85. * This constructor is only available when the types that the pair stores
  86. * are both at least default constructible.
  87. *
  88. * @tparam Dummy Dummy template parameter used for internal purposes.
  89. */
  90. template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
  91. constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
  92. : first_base{},
  93. second_base{} {}
  94. /**
  95. * @brief Copy constructor.
  96. * @param other The instance to copy from.
  97. */
  98. constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
  99. /**
  100. * @brief Move constructor.
  101. * @param other The instance to move from.
  102. */
  103. constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
  104. /**
  105. * @brief Constructs a pair from its values.
  106. * @tparam Arg Type of value to use to initialize the first element.
  107. * @tparam Other Type of value to use to initialize the second element.
  108. * @param arg Value to use to initialize the first element.
  109. * @param other Value to use to initialize the second element.
  110. */
  111. template<typename Arg, typename Other>
  112. constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
  113. : first_base{std::forward<Arg>(arg)},
  114. second_base{std::forward<Other>(other)} {}
  115. /**
  116. * @brief Constructs a pair by forwarding the arguments to its parts.
  117. * @tparam Args Types of arguments to use to initialize the first element.
  118. * @tparam Other Types of arguments to use to initialize the second element.
  119. * @param args Arguments to use to initialize the first element.
  120. * @param other Arguments to use to initialize the second element.
  121. */
  122. template<typename... Args, typename... Other>
  123. constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
  124. : first_base{std::move(args), std::index_sequence_for<Args...>{}},
  125. second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
  126. /**
  127. * @brief Copy assignment operator.
  128. * @param other The instance to copy from.
  129. * @return This compressed pair object.
  130. */
  131. constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
  132. /**
  133. * @brief Move assignment operator.
  134. * @param other The instance to move from.
  135. * @return This compressed pair object.
  136. */
  137. constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
  138. /**
  139. * @brief Returns the first element that a pair stores.
  140. * @return The first element that a pair stores.
  141. */
  142. [[nodiscard]] constexpr first_type &first() noexcept {
  143. return static_cast<first_base &>(*this).get();
  144. }
  145. /*! @copydoc first */
  146. [[nodiscard]] constexpr const first_type &first() const noexcept {
  147. return static_cast<const first_base &>(*this).get();
  148. }
  149. /**
  150. * @brief Returns the second element that a pair stores.
  151. * @return The second element that a pair stores.
  152. */
  153. [[nodiscard]] constexpr second_type &second() noexcept {
  154. return static_cast<second_base &>(*this).get();
  155. }
  156. /*! @copydoc second */
  157. [[nodiscard]] constexpr const second_type &second() const noexcept {
  158. return static_cast<const second_base &>(*this).get();
  159. }
  160. /**
  161. * @brief Swaps two compressed pair objects.
  162. * @param other The compressed pair to swap with.
  163. */
  164. constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
  165. using std::swap;
  166. swap(first(), other.first());
  167. swap(second(), other.second());
  168. }
  169. /**
  170. * @brief Extracts an element from the compressed pair.
  171. * @tparam Index An integer value that is either 0 or 1.
  172. * @return Returns a reference to the first element if `Index` is 0 and a
  173. * reference to the second element if `Index` is 1.
  174. */
  175. template<std::size_t Index>
  176. constexpr decltype(auto) get() noexcept {
  177. if constexpr(Index == 0u) {
  178. return first();
  179. } else {
  180. static_assert(Index == 1u, "Index out of bounds");
  181. return second();
  182. }
  183. }
  184. /*! @copydoc get */
  185. template<std::size_t Index>
  186. constexpr decltype(auto) get() const noexcept {
  187. if constexpr(Index == 0u) {
  188. return first();
  189. } else {
  190. static_assert(Index == 1u, "Index out of bounds");
  191. return second();
  192. }
  193. }
  194. };
  195. /**
  196. * @brief Deduction guide.
  197. * @tparam Type Type of value to use to initialize the first element.
  198. * @tparam Other Type of value to use to initialize the second element.
  199. */
  200. template<typename Type, typename Other>
  201. compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
  202. /**
  203. * @brief Swaps two compressed pair objects.
  204. * @tparam First The type of the first element that the pairs store.
  205. * @tparam Second The type of the second element that the pairs store.
  206. * @param lhs A valid compressed pair object.
  207. * @param rhs A valid compressed pair object.
  208. */
  209. template<typename First, typename Second>
  210. inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
  211. lhs.swap(rhs);
  212. }
  213. } // namespace entt
  214. // disable structured binding support for clang 6, it messes when specializing tuple_size
  215. #if !defined __clang_major__ || __clang_major__ > 6
  216. namespace std {
  217. /**
  218. * @brief `std::tuple_size` specialization for `compressed_pair`s.
  219. * @tparam First The type of the first element that the pair stores.
  220. * @tparam Second The type of the second element that the pair stores.
  221. */
  222. template<typename First, typename Second>
  223. struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
  224. /**
  225. * @brief `std::tuple_element` specialization for `compressed_pair`s.
  226. * @tparam Index The index of the type to return.
  227. * @tparam First The type of the first element that the pair stores.
  228. * @tparam Second The type of the second element that the pair stores.
  229. */
  230. template<size_t Index, typename First, typename Second>
  231. struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
  232. static_assert(Index < 2u, "Index out of bounds");
  233. };
  234. } // namespace std
  235. #endif
  236. #endif