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 <__type_traits/is_nothrow_copy_constructible.h> 21 #include <__type_traits/is_nothrow_default_constructible.h> 22 #include <__utility/move.h> 23 #include <optional> 24 25 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 26 # pragma GCC system_header 27 #endif 28 29 _LIBCPP_BEGIN_NAMESPACE_STD 30 31 #if _LIBCPP_STD_VER >= 20 32 33 // __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into 34 // a type that is both move-constructible and move-assignable. It does that by introducing an empty state 35 // and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary 36 // to handle the case where the copy construction fails after destroying the object. 37 // 38 // In some cases, we can completely avoid the use of an empty state; we provide a specialization of 39 // __movable_box that does this, see below for the details. 40 41 // until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not 42 // just move-constructible; we preserve the old behavior in pre-C++23 modes. 43 template <class _Tp> 44 concept __movable_box_object = 45 # if _LIBCPP_STD_VER >= 23 46 move_constructible<_Tp> 47 # else 48 copy_constructible<_Tp> 49 # endif 50 && is_object_v<_Tp>; 51 52 namespace ranges { 53 // Primary template - uses std::optional and introduces an empty state in case assignment fails. 54 template <__movable_box_object _Tp> 55 class __movable_box { 56 _LIBCPP_NO_UNIQUE_ADDRESS optional<_Tp> __val_; 57 58 public: 59 template <class... _Args> 60 requires is_constructible_v<_Tp, _Args...> 61 _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept( 62 is_nothrow_constructible_v<_Tp, _Args...>) 63 : __val_(in_place, std::forward<_Args>(__args)...) {} 64 65 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) 66 requires default_initializable<_Tp> 67 : __val_(in_place) {} 68 69 _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default; 70 _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; 71 72 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& 73 operator=(__movable_box const& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>) 74 # if _LIBCPP_STD_VER >= 23 75 requires copy_constructible<_Tp> 76 # endif 77 { 78 if (this != std::addressof(__other)) { 79 if (__other.__has_value()) 80 __val_.emplace(*__other); 81 else 82 __val_.reset(); 83 } 84 return *this; 85 } 86 87 _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&) 88 requires movable<_Tp> 89 = default; 90 91 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& 92 operator=(__movable_box&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) { 93 if (this != std::addressof(__other)) { 94 if (__other.__has_value()) 95 __val_.emplace(std::move(*__other)); 96 else 97 __val_.reset(); 98 } 99 return *this; 100 } 101 102 _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return *__val_; } 103 _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return *__val_; } 104 105 _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return __val_.operator->(); } 106 _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return __val_.operator->(); } 107 108 _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return __val_.has_value(); } 109 }; 110 111 // This partial specialization implements an optimization for when we know we don't need to store 112 // an empty state to represent failure to perform an assignment. For copy-assignment, this happens: 113 // 114 // 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator 115 // directly and avoid using std::optional. 116 // 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as 117 // destroy-and-then-construct and we know it will never fail, so we don't need an empty state. 118 // 119 // The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and 120 // nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled 121 // whenever we can apply any of these optimizations for both the copy assignment and the move assignment 122 // operator. 123 124 # if _LIBCPP_STD_VER >= 23 125 template <class _Tp> 126 concept __doesnt_need_empty_state = 127 (copy_constructible<_Tp> 128 // 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models 129 // copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true. 130 ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>) 131 // 2. Otherwise, movable-box<T> should store only a T if either T models movable or 132 // is_nothrow_move_constructible_v<T> is true. 133 : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>); 134 # else 135 136 template <class _Tp> 137 concept __doesnt_need_empty_state_for_copy = copyable<_Tp> || is_nothrow_copy_constructible_v<_Tp>; 138 139 template <class _Tp> 140 concept __doesnt_need_empty_state_for_move = movable<_Tp> || is_nothrow_move_constructible_v<_Tp>; 141 142 template <class _Tp> 143 concept __doesnt_need_empty_state = __doesnt_need_empty_state_for_copy<_Tp> && __doesnt_need_empty_state_for_move<_Tp>; 144 # endif 145 146 template <__movable_box_object _Tp> 147 requires __doesnt_need_empty_state<_Tp> 148 class __movable_box<_Tp> { 149 _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_; 150 151 public: 152 template <class... _Args> 153 requires is_constructible_v<_Tp, _Args...> 154 _LIBCPP_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept( 155 is_nothrow_constructible_v<_Tp, _Args...>) 156 : __val_(std::forward<_Args>(__args)...) {} 157 158 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box() noexcept(is_nothrow_default_constructible_v<_Tp>) 159 requires default_initializable<_Tp> 160 : __val_() {} 161 162 _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box const&) = default; 163 _LIBCPP_HIDE_FROM_ABI __movable_box(__movable_box&&) = default; 164 165 // Implementation of assignment operators in case we perform optimization (1) 166 _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box const&) 167 requires copyable<_Tp> 168 = default; 169 _LIBCPP_HIDE_FROM_ABI __movable_box& operator=(__movable_box&&) 170 requires movable<_Tp> 171 = default; 172 173 // Implementation of assignment operators in case we perform optimization (2) 174 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box const& __other) noexcept { 175 static_assert(is_nothrow_copy_constructible_v<_Tp>); 176 if (this != std::addressof(__other)) { 177 std::destroy_at(std::addressof(__val_)); 178 std::construct_at(std::addressof(__val_), __other.__val_); 179 } 180 return *this; 181 } 182 183 _LIBCPP_HIDE_FROM_ABI constexpr __movable_box& operator=(__movable_box&& __other) noexcept { 184 static_assert(is_nothrow_move_constructible_v<_Tp>); 185 if (this != std::addressof(__other)) { 186 std::destroy_at(std::addressof(__val_)); 187 std::construct_at(std::addressof(__val_), std::move(__other.__val_)); 188 } 189 return *this; 190 } 191 192 _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept { return __val_; } 193 _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept { return __val_; } 194 195 _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { return std::addressof(__val_); } 196 _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { return std::addressof(__val_); } 197 198 _LIBCPP_HIDE_FROM_ABI constexpr bool __has_value() const noexcept { return true; } 199 }; 200 } // namespace ranges 201 202 #endif // _LIBCPP_STD_VER >= 20 203 204 _LIBCPP_END_NAMESPACE_STD 205 206 #endif // _LIBCPP___RANGES_MOVABLE_BOX_H 207