movable_box.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // -*- C++ -*-
  2. //===----------------------------------------------------------------------===//
  3. //
  4. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  5. // See https://llvm.org/LICENSE.txt for license information.
  6. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  7. //
  8. //===----------------------------------------------------------------------===//
  9. #ifndef _LIBCPP___RANGES_MOVABLE_BOX_H
  10. #define _LIBCPP___RANGES_MOVABLE_BOX_H
  11. #include <__concepts/constructible.h>
  12. #include <__concepts/copyable.h>
  13. #include <__concepts/movable.h>
  14. #include <__config>
  15. #include <__memory/addressof.h>
  16. #include <__memory/construct_at.h>
  17. #include <__type_traits/is_nothrow_constructible.h>
  18. #include <__utility/move.h>
  19. #include <optional>
  20. #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
  21. # pragma GCC system_header
  22. #endif
  23. _LIBCPP_PUSH_MACROS
  24. #include <__undef_macros>
  25. _LIBCPP_BEGIN_NAMESPACE_STD
  26. #if _LIBCPP_STD_VER >= 20
  27. // __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into
  28. // a type that is both move-constructible and move-assignable. It does that by introducing an empty state
  29. // and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary
  30. // to handle the case where the copy construction fails after destroying the object.
  31. //
  32. // In some cases, we can completely avoid the use of an empty state; we provide a specialization of
  33. // __movable_box that does this, see below for the details.
  34. // until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not
  35. // just move-constructible; we preserve the old behavior in pre-C++23 modes.
  36. template <class _Tp>
  37. concept __movable_box_object =
  38. # if _LIBCPP_STD_VER >= 23
  39. move_constructible<_Tp>
  40. # else
  41. copy_constructible<_Tp>
  42. # endif
  43. && is_object_v<_Tp>;
  44. namespace ranges {
  45. // Primary template - uses std::optional and introduces an empty state in case assignment fails.
  46. template <__movable_box_object _Tp>
  47. class __movable_box {
  48. _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_;
  49. public:
  50. template <class... _Args>
  51. requires is_constructible_v<_Tp, _Args...>
  52. _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept(
  53. is_nothrow_constructible_v<_Tp, _Args...>)
  54. : __val_(in_place, std::forward<_Args>(__args)...) {}
  55. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
  56. requires default_initializable<_Tp>
  57. : __val_(in_place) {}
  58. _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default;
  59. _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default;
  60. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box&
  61. operator=(__movable_box const& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>)
  62. # if _LIBCPP_STD_VER >= 23
  63. requires copy_constructible<_Tp>
  64. # endif
  65. {
  66. if (this != std::addressof(__other)) {
  67. if (__other.__has_value())
  68. __val_.emplace(*__other);
  69. else
  70. __val_.reset();
  71. }
  72. return *this;
  73. }
  74. _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&)
  75. requires movable<_Tp>
  76. = default;
  77. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box&
  78. operator=(__movable_box&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) {
  79. if (this != std::addressof(__other)) {
  80. if (__other.__has_value())
  81. __val_.emplace(std::move(*__other));
  82. else
  83. __val_.reset();
  84. }
  85. return *this;
  86. }
  87. _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; }
  88. _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; }
  89. _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return __val_.operator->(); }
  90. _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return __val_.operator->(); }
  91. _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); }
  92. };
  93. // This partial specialization implements an optimization for when we know we don't need to store
  94. // an empty state to represent failure to perform an assignment. For copy-assignment, this happens:
  95. //
  96. // 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator
  97. // directly and avoid using std::optional.
  98. // 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as
  99. // destroy-and-then-construct and we know it will never fail, so we don't need an empty state.
  100. //
  101. // The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and
  102. // nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled
  103. // whenever we can apply any of these optimizations for both the copy assignment and the move assignment
  104. // operator.
  105. # if _LIBCPP_STD_VER >= 23
  106. template <class _Tp>
  107. concept __doesnt_need_empty_state =
  108. (copy_constructible<_Tp>
  109. // 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models
  110. // copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true.
  111. ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>)
  112. // 2. Otherwise, movable-box<T> should store only a T if either T models movable or
  113. // is_nothrow_move_constructible_v<T> is true.
  114. : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>);
  115. // When _Tp doesn't have an assignment operator, we must implement __movable_box's assignment operator
  116. // by doing destroy_at followed by construct_at. However, that implementation strategy leads to UB if the nested
  117. // _Tp is potentially overlapping, as it is doing a non-transparent replacement of the sub-object, which means that
  118. // we're not considered "nested" inside the movable-box anymore, and since we're not nested within it, [basic.life]/1.5
  119. // says that we essentially just reused the storage of the movable-box for a completely unrelated object and ended the
  120. // movable-box's lifetime.
  121. // https://github.com/llvm/llvm-project/issues/70494#issuecomment-1845646490
  122. //
  123. // Hence, when the _Tp doesn't have an assignment operator, we can't risk making it a potentially-overlapping
  124. // subobject because of the above, and we don't use [[no_unique_address]] in that case.
  125. template <class _Tp>
  126. concept __can_use_no_unique_address = (copy_constructible<_Tp> ? copyable<_Tp> : movable<_Tp>);
  127. # else
  128. template <class _Tp>
  129. concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>;
  130. template <class _Tp>
  131. concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>;
  132. template <class _Tp>
  133. concept __doesnt_need_empty_state = __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>;
  134. template <class _Tp>
  135. concept __can_use_no_unique_address = copyable<_Tp>;
  136. # endif
  137. template <class _Tp>
  138. struct __movable_box_holder {
  139. _Tp __val_;
  140. template <class... _Args>
  141. _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box_holder(in_place_t, _Args&&... __args)
  142. : __val_(std::forward<_Args>(__args)...) {}
  143. };
  144. template <class _Tp>
  145. requires __can_use_no_unique_address<_Tp>
  146. struct __movable_box_holder<_Tp> {
  147. _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_;
  148. template <class... _Args>
  149. _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box_holder(in_place_t, _Args&&... __args)
  150. : __val_(std::forward<_Args>(__args)...) {}
  151. };
  152. template <__movable_box_object _Tp>
  153. requires __doesnt_need_empty_state<_Tp>
  154. class __movable_box<_Tp> {
  155. _LIBCPP_NO_UNIQUE_ADDRESS __movable_box_holder<_Tp> __holder_;
  156. public:
  157. template <class... _Args>
  158. requires is_constructible_v<_Tp, _Args...>
  159. _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t __inplace, _Args&&... __args) noexcept(
  160. is_nothrow_constructible_v<_Tp, _Args...>)
  161. : __holder_(__inplace, std::forward<_Args>(__args)...) {}
  162. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
  163. requires default_initializable<_Tp>
  164. : __holder_(in_place_t{}) {}
  165. _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default;
  166. _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default;
  167. // Implementation of assignment operators in case we perform optimization (1)
  168. _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box const&)
  169. requires copyable<_Tp>
  170. = default;
  171. _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&)
  172. requires movable<_Tp>
  173. = default;
  174. // Implementation of assignment operators in case we perform optimization (2)
  175. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box const& __other) noexcept {
  176. static_assert(is_nothrow_copy_constructible_v<_Tp>);
  177. static_assert(!__can_use_no_unique_address<_Tp>);
  178. if (this != std::addressof(__other)) {
  179. std::destroy_at(std::addressof(__holder_.__val_));
  180. std::construct_at(std::addressof(__holder_.__val_), __other.__holder_.__val_);
  181. }
  182. return *this;
  183. }
  184. _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box&& __other) noexcept {
  185. static_assert(is_nothrow_move_constructible_v<_Tp>);
  186. static_assert(!__can_use_no_unique_address<_Tp>);
  187. if (this != std::addressof(__other)) {
  188. std::destroy_at(std::addressof(__holder_.__val_));
  189. std::construct_at(std::addressof(__holder_.__val_), std::move(__other.__holder_.__val_));
  190. }
  191. return *this;
  192. }
  193. _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __holder_.__val_; }
  194. _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __holder_.__val_; }
  195. _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return std::addressof(__holder_.__val_); }
  196. _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return std::addressof(__holder_.__val_); }
  197. _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; }
  198. };
  199. } // namespace ranges
  200. #endif // _LIBCPP_STD_VER >= 20
  201. _LIBCPP_END_NAMESPACE_STD
  202. _LIBCPP_POP_MACROS
  203. #endif // _LIBCPP___RANGES_MOVABLE_BOX_H