xref: /openbsd-src/gnu/llvm/libcxx/include/__ranges/copyable_box.h (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
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_COPYABLE_BOX_H
11 #define _LIBCPP___RANGES_COPYABLE_BOX_H
12 
13 #include <__config>
14 #include <__memory/addressof.h>
15 #include <__memory/construct_at.h>
16 #include <__utility/move.h>
17 #include <concepts>
18 #include <optional>
19 #include <type_traits>
20 
21 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
22 #pragma GCC system_header
23 #endif
24 
25 _LIBCPP_PUSH_MACROS
26 #include <__undef_macros>
27 
28 _LIBCPP_BEGIN_NAMESPACE_STD
29 
30 #if !defined(_LIBCPP_HAS_NO_RANGES)
31 
32 // __copyable_box allows turning a type that is copy-constructible (but maybe not copy-assignable) into
33 // a type that is both copy-constructible and copy-assignable. It does that by introducing an empty state
34 // and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary
35 // to handle the case where the copy construction fails after destroying the object.
36 //
37 // In some cases, we can completely avoid the use of an empty state; we provide a specialization of
38 // __copyable_box that does this, see below for the details.
39 
40 template<class _Tp>
41 concept __copy_constructible_object = copy_constructible<_Tp> && is_object_v<_Tp>;
42 
43 namespace ranges {
44   // Primary template - uses std::optional and introduces an empty state in case assignment fails.
45   template<__copy_constructible_object _Tp>
46   class __copyable_box {
47     [[no_unique_address]] optional<_Tp> __val_;
48 
49   public:
50     template<class ..._Args>
51       requires is_constructible_v<_Tp, _Args...>
52     _LIBCPP_HIDE_FROM_ABI
53     constexpr explicit __copyable_box(in_place_t, _Args&& ...__args)
54       noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
55       : __val_(in_place, _VSTD::forward<_Args>(__args)...)
56     { }
57 
58     _LIBCPP_HIDE_FROM_ABI
59     constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
60       requires default_initializable<_Tp>
61       : __val_(in_place)
62     { }
63 
64     _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box const&) = default;
65     _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box&&) = default;
66 
67     _LIBCPP_HIDE_FROM_ABI
68     constexpr __copyable_box& operator=(__copyable_box const& __other)
69       noexcept(is_nothrow_copy_constructible_v<_Tp>)
70     {
71       if (this != _VSTD::addressof(__other)) {
72         if (__other.__has_value()) __val_.emplace(*__other);
73         else                       __val_.reset();
74       }
75       return *this;
76     }
77 
78     _LIBCPP_HIDE_FROM_ABI
79     __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default;
80 
81     _LIBCPP_HIDE_FROM_ABI
82     constexpr __copyable_box& operator=(__copyable_box&& __other)
83       noexcept(is_nothrow_move_constructible_v<_Tp>)
84     {
85       if (this != _VSTD::addressof(__other)) {
86         if (__other.__has_value()) __val_.emplace(_VSTD::move(*__other));
87         else                       __val_.reset();
88       }
89       return *this;
90     }
91 
92     _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; }
93     _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; }
94     _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); }
95   };
96 
97   // This partial specialization implements an optimization for when we know we don't need to store
98   // an empty state to represent failure to perform an assignment. For copy-assignment, this happens:
99   //
100   // 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator
101   //    directly and avoid using std::optional.
102   // 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as
103   //    destroy-and-then-construct and we know it will never fail, so we don't need an empty state.
104   //
105   // The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and
106   // nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled
107   // whenever we can apply any of these optimizations for both the copy assignment and the move assignment
108   // operator.
109   template<class _Tp>
110   concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>;
111 
112   template<class _Tp>
113   concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>;
114 
115   template<__copy_constructible_object _Tp>
116     requires __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>
117   class __copyable_box<_Tp> {
118     [[no_unique_address]] _Tp __val_;
119 
120   public:
121     template<class ..._Args>
122       requires is_constructible_v<_Tp, _Args...>
123     _LIBCPP_HIDE_FROM_ABI
124     constexpr explicit __copyable_box(in_place_t, _Args&& ...__args)
125       noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
126       : __val_(_VSTD::forward<_Args>(__args)...)
127     { }
128 
129     _LIBCPP_HIDE_FROM_ABI
130     constexpr __copyable_box() noexcept(is_nothrow_default_constructible_v<_Tp>)
131       requires default_initializable<_Tp>
132       : __val_()
133     { }
134 
135     _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box const&) = default;
136     _LIBCPP_HIDE_FROM_ABI __copyable_box(__copyable_box&&) = default;
137 
138     // Implementation of assignment operators in case we perform optimization (1)
139     _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box const&) requires copyable<_Tp> = default;
140     _LIBCPP_HIDE_FROM_ABI __copyable_box& operator=(__copyable_box&&) requires movable<_Tp> = default;
141 
142     // Implementation of assignment operators in case we perform optimization (2)
143     _LIBCPP_HIDE_FROM_ABI
144     constexpr __copyable_box& operator=(__copyable_box const& __other) noexcept {
145       static_assert(is_nothrow_copy_constructible_v<_Tp>);
146       if (this != _VSTD::addressof(__other)) {
147         _VSTD::destroy_at(_VSTD::addressof(__val_));
148         _VSTD::construct_at(_VSTD::addressof(__val_), __other.__val_);
149       }
150       return *this;
151     }
152 
153     _LIBCPP_HIDE_FROM_ABI
154     constexpr __copyable_box& operator=(__copyable_box&& __other) noexcept {
155       static_assert(is_nothrow_move_constructible_v<_Tp>);
156       if (this != _VSTD::addressof(__other)) {
157         _VSTD::destroy_at(_VSTD::addressof(__val_));
158         _VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::move(__other.__val_));
159       }
160       return *this;
161     }
162 
163     _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; }
164     _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; }
165     _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; }
166   };
167 } // namespace ranges
168 
169 #endif // !defined(_LIBCPP_HAS_NO_RANGES)
170 
171 _LIBCPP_END_NAMESPACE_STD
172 
173 _LIBCPP_POP_MACROS
174 
175 #endif // _LIBCPP___RANGES_COPYABLE_BOX_H
176