Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/libcxx/include/__ranges/movable_box.h
35236 views
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
249