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