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