|
| 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 | +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. |
| 8 | +// |
| 9 | +//===----------------------------------------------------------------------===// |
| 10 | + |
| 11 | +#ifndef _LIBCUDACXX___RANGES_MOVABLE_BOX_H |
| 12 | +#define _LIBCUDACXX___RANGES_MOVABLE_BOX_H |
| 13 | + |
| 14 | +#include <cuda/std/detail/__config> |
| 15 | + |
| 16 | +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) |
| 17 | +# pragma GCC system_header |
| 18 | +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) |
| 19 | +# pragma clang system_header |
| 20 | +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) |
| 21 | +# pragma system_header |
| 22 | +#endif // no system header |
| 23 | + |
| 24 | +#include <cuda/std/__concepts/constructible.h> |
| 25 | +#include <cuda/std/__concepts/copyable.h> |
| 26 | +#include <cuda/std/__concepts/movable.h> |
| 27 | +#include <cuda/std/__memory/addressof.h> |
| 28 | +#include <cuda/std/__memory/construct_at.h> |
| 29 | +#include <cuda/std/__tuple_dir/sfinae_helpers.h> |
| 30 | +#include <cuda/std/__type_traits/is_constructible.h> |
| 31 | +#include <cuda/std/__type_traits/is_nothrow_constructible.h> |
| 32 | +#include <cuda/std/__type_traits/is_nothrow_copy_constructible.h> |
| 33 | +#include <cuda/std/__type_traits/is_nothrow_default_constructible.h> |
| 34 | +#include <cuda/std/__type_traits/is_nothrow_move_constructible.h> |
| 35 | +#include <cuda/std/__utility/move.h> |
| 36 | +#include <cuda/std/detail/libcxx/include/optional> |
| 37 | + |
| 38 | +_LIBCUDACXX_BEGIN_NAMESPACE_RANGES |
| 39 | + |
| 40 | +// __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into |
| 41 | +// a type that is both move-constructible and move-assignable. It does that by introducing an empty state |
| 42 | +// and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary |
| 43 | +// to handle the case where the copy construction fails after destroying the object. |
| 44 | +// |
| 45 | +// In some cases, we can completely avoid the use of an empty state; we provide a specialization of |
| 46 | +// __movable_box that does this, see below for the details. |
| 47 | + |
| 48 | +// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not |
| 49 | +// just move-constructible; we always use the C++23 behavior |
| 50 | +template <class _Tp> |
| 51 | +_CCCL_CONCEPT __movable_box_object = move_constructible<_Tp> && is_object_v<_Tp>; |
| 52 | + |
| 53 | +// The partial specialization implements an optimization for when we know we don't need to store |
| 54 | +// an empty state to represent failure to perform an assignment. For copy-assignment, this happens: |
| 55 | +// |
| 56 | +// 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator |
| 57 | +// directly and avoid using _CUDA_VSTD::optional. |
| 58 | +// 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as |
| 59 | +// destroy-and-then-construct and we know it will never fail, so we don't need an empty state. |
| 60 | +// |
| 61 | +// The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and |
| 62 | +// nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled |
| 63 | +// whenever we can apply any of these optimizations for both the copy assignment and the move assignment |
| 64 | +// operator. |
| 65 | + |
| 66 | +template <class _Tp> |
| 67 | +_CCCL_CONCEPT __doesnt_need_empty_state = |
| 68 | + (copy_constructible<_Tp> |
| 69 | + // 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models |
| 70 | + // copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true. |
| 71 | + ? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>) |
| 72 | + // 2. Otherwise, movable-box<T> should store only a T if either T models movable or |
| 73 | + // is_nothrow_move_constructible_v<T> is true. |
| 74 | + : movable<_Tp> || is_nothrow_move_constructible_v<_Tp>); |
| 75 | + |
| 76 | +// When _Tp doesn't have an assignment operator, we must implement __movable_box's assignment operator |
| 77 | +// by doing destroy_at followed by construct_at. However, that implementation strategy leads to UB if the nested |
| 78 | +// _Tp is potentially overlapping, as it is doing a non-transparent replacement of the sub-object, which means that |
| 79 | +// we're not considered "nested" inside the movable-box anymore, and since we're not nested within it, [basic.life]/1.5 |
| 80 | +// says that we essentially just reused the storage of the movable-box for a completely unrelated object and ended the |
| 81 | +// movable-box's lifetime. |
| 82 | +// https://github.com/llvm/llvm-project/issues/70494#issuecomment-1845646490 |
| 83 | +// |
| 84 | +// Hence, when the _Tp doesn't have an assignment operator, we can't risk making it a potentially-overlapping |
| 85 | +// subobject because of the above, and we don't use [[no_unique_address]] in that case. |
| 86 | +template <class _Tp> |
| 87 | +_CCCL_CONCEPT __can_use_no_unique_address = (copy_constructible<_Tp> ? copyable<_Tp> : movable<_Tp>); |
| 88 | + |
| 89 | +// base class |
| 90 | + |
| 91 | +template <class _Tp, bool = default_initializable<_Tp>> |
| 92 | +struct __mb_optional_destruct_base |
| 93 | +{ |
| 94 | + _CCCL_NO_UNIQUE_ADDRESS optional<_Tp> __val_; |
| 95 | + |
| 96 | + _CCCL_TEMPLATE(class... _Args) |
| 97 | + _CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>) |
| 98 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_optional_destruct_base(in_place_t, _Args&&... __args) noexcept( |
| 99 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 100 | + : __val_(in_place, _CUDA_VSTD::forward<_Args>(__args)...) |
| 101 | + {} |
| 102 | +}; |
| 103 | + |
| 104 | +template <class _Tp> |
| 105 | +struct __mb_optional_destruct_base<_Tp, true> |
| 106 | +{ |
| 107 | + _CCCL_NO_UNIQUE_ADDRESS optional<_Tp> __val_; |
| 108 | + |
| 109 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_destruct_base() noexcept(is_nothrow_default_constructible_v<_Tp>) |
| 110 | + : __val_(in_place) |
| 111 | + {} |
| 112 | + |
| 113 | + _CCCL_TEMPLATE(class... _Args) |
| 114 | + _CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>) |
| 115 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_optional_destruct_base(in_place_t, _Args&&... __args) noexcept( |
| 116 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 117 | + : __val_(in_place, _CUDA_VSTD::forward<_Args>(__args)...) |
| 118 | + {} |
| 119 | +}; |
| 120 | + |
| 121 | +template <class _Tp, bool = copy_constructible<_Tp>> |
| 122 | +struct __mb_optional_copy_assign : __mb_optional_destruct_base<_Tp> |
| 123 | +{ |
| 124 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_copy_assign, __mb_optional_destruct_base, _Tp); |
| 125 | + |
| 126 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(const __mb_optional_copy_assign&) = default; |
| 127 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(__mb_optional_copy_assign&&) = default; |
| 128 | + |
| 129 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(const __mb_optional_copy_assign&) = delete; |
| 130 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(__mb_optional_copy_assign&&) = default; |
| 131 | +}; |
| 132 | + |
| 133 | +template <class _Tp> |
| 134 | +struct __mb_optional_copy_assign<_Tp, true> : __mb_optional_destruct_base<_Tp> |
| 135 | +{ |
| 136 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_copy_assign, __mb_optional_destruct_base, _Tp); |
| 137 | + |
| 138 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(const __mb_optional_copy_assign&) = default; |
| 139 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(__mb_optional_copy_assign&&) = default; |
| 140 | + |
| 141 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& |
| 142 | + operator=(const __mb_optional_copy_assign& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>) |
| 143 | + { |
| 144 | + if (this != _CUDA_VSTD::addressof(__other)) |
| 145 | + { |
| 146 | + if (__other.__has_value()) |
| 147 | + { |
| 148 | + this->__val_.emplace(*__other); |
| 149 | + } |
| 150 | + else |
| 151 | + { |
| 152 | + this->__val_.reset(); |
| 153 | + } |
| 154 | + } |
| 155 | + return *this; |
| 156 | + }; |
| 157 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(__mb_optional_copy_assign&&) = default; |
| 158 | +}; |
| 159 | + |
| 160 | +template <class _Tp, bool = movable<_Tp>> |
| 161 | +struct __mb_optional_move_assign : __mb_optional_copy_assign<_Tp> |
| 162 | +{ |
| 163 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_move_assign, __mb_optional_copy_assign, _Tp); |
| 164 | +}; |
| 165 | + |
| 166 | +template <class _Tp> |
| 167 | +struct __mb_optional_move_assign<_Tp, false> : __mb_optional_copy_assign<_Tp> |
| 168 | +{ |
| 169 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_move_assign, __mb_optional_copy_assign, _Tp); |
| 170 | + |
| 171 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign(const __mb_optional_move_assign&) = default; |
| 172 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign(__mb_optional_move_assign&&) = default; |
| 173 | + _CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign& operator=(const __mb_optional_move_assign&) = default; |
| 174 | + |
| 175 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_move_assign& |
| 176 | + operator=(__mb_optional_move_assign&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) |
| 177 | + { |
| 178 | + if (this != _CUDA_VSTD::addressof(__other)) |
| 179 | + { |
| 180 | + if (__other.__has_value()) |
| 181 | + { |
| 182 | + this->__val_.emplace(_CUDA_VSTD::move(*__other)); |
| 183 | + } |
| 184 | + else |
| 185 | + { |
| 186 | + this->__val_.reset(); |
| 187 | + } |
| 188 | + } |
| 189 | + return *this; |
| 190 | + } |
| 191 | +}; |
| 192 | + |
| 193 | +template <class _Tp> |
| 194 | +struct __mb_optional_base |
| 195 | + : __mb_optional_move_assign<_Tp> |
| 196 | + , __sfinae_move_base<copy_constructible<_Tp>, true> |
| 197 | +{ |
| 198 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_base, __mb_optional_move_assign, _Tp); |
| 199 | + |
| 200 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept |
| 201 | + { |
| 202 | + return *this->__val_; |
| 203 | + } |
| 204 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept |
| 205 | + { |
| 206 | + return *this->__val_; |
| 207 | + } |
| 208 | + |
| 209 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept |
| 210 | + { |
| 211 | + return this->__val_.operator->(); |
| 212 | + } |
| 213 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept |
| 214 | + { |
| 215 | + return this->__val_.operator->(); |
| 216 | + } |
| 217 | + |
| 218 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr bool __has_value() const noexcept |
| 219 | + { |
| 220 | + return this->__val_.has_value(); |
| 221 | + } |
| 222 | +}; |
| 223 | + |
| 224 | +// Specialization without a boolean |
| 225 | +template <class _Tp, bool = __can_use_no_unique_address<_Tp>> |
| 226 | +struct __mb_holder |
| 227 | +{ |
| 228 | + _Tp __val_; |
| 229 | + |
| 230 | + template <class... _Args> |
| 231 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder(in_place_t, _Args&&... __args) noexcept( |
| 232 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 233 | + : __val_(_CUDA_VSTD::forward<_Args>(__args)...) |
| 234 | + {} |
| 235 | +}; |
| 236 | + |
| 237 | +template <class _Tp> |
| 238 | +struct __mb_holder<_Tp, true> |
| 239 | +{ |
| 240 | + _CCCL_NO_UNIQUE_ADDRESS _Tp __val_; |
| 241 | + |
| 242 | + template <class... _Args> |
| 243 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder(in_place_t, _Args&&... __args) noexcept( |
| 244 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 245 | + : __val_(_CUDA_VSTD::forward<_Args>(__args)...) |
| 246 | + {} |
| 247 | +}; |
| 248 | + |
| 249 | +template <class _Tp, bool = default_initializable<_Tp>> |
| 250 | +struct __mb_holder_base |
| 251 | +{ |
| 252 | + _CCCL_NO_UNIQUE_ADDRESS __mb_holder<_Tp> __holder_; |
| 253 | + |
| 254 | + _CCCL_TEMPLATE(class... _Args) |
| 255 | + _CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>) |
| 256 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder_base(in_place_t, _Args&&... __args) noexcept( |
| 257 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 258 | + : __holder_(in_place, _CUDA_VSTD::forward<_Args>(__args)...) |
| 259 | + {} |
| 260 | +}; |
| 261 | + |
| 262 | +template <class _Tp> |
| 263 | +struct __mb_holder_base<_Tp, true> |
| 264 | +{ |
| 265 | + _CCCL_NO_UNIQUE_ADDRESS __mb_holder<_Tp> __holder_; |
| 266 | + |
| 267 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_holder_base() noexcept(is_nothrow_default_constructible_v<_Tp>) |
| 268 | + : __holder_(in_place) |
| 269 | + {} |
| 270 | + |
| 271 | + _CCCL_TEMPLATE(class... _Args) |
| 272 | + _CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>) |
| 273 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder_base(in_place_t, _Args&&... __args) noexcept( |
| 274 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 275 | + : __holder_(in_place, _CUDA_VSTD::forward<_Args>(__args)...) |
| 276 | + {} |
| 277 | +}; |
| 278 | + |
| 279 | +template <class _Tp, bool = copyable<_Tp>> |
| 280 | +struct __mb_copy_assign : __mb_holder_base<_Tp> |
| 281 | +{ |
| 282 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_copy_assign, __mb_holder_base, _Tp); |
| 283 | +}; |
| 284 | + |
| 285 | +template <class _Tp> |
| 286 | +struct __mb_copy_assign<_Tp, false> : __mb_holder_base<_Tp> |
| 287 | +{ |
| 288 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_copy_assign, __mb_holder_base, _Tp); |
| 289 | + |
| 290 | + _CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign(const __mb_copy_assign&) = default; |
| 291 | + _CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign(__mb_copy_assign&&) = default; |
| 292 | + |
| 293 | + _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX20 __mb_copy_assign& operator=(const __mb_copy_assign& __other) noexcept |
| 294 | + { |
| 295 | + static_assert(is_nothrow_copy_constructible_v<_Tp>); |
| 296 | + static_assert(!__can_use_no_unique_address<_Tp>); |
| 297 | + if (this != _CUDA_VSTD::addressof(__other)) |
| 298 | + { |
| 299 | + _CUDA_VSTD::__destroy_at(_CUDA_VSTD::addressof(this->__holder_.__val_)); |
| 300 | + _CUDA_VSTD::__construct_at(_CUDA_VSTD::addressof(this->__holder_.__val_), __other.__holder_.__val_); |
| 301 | + } |
| 302 | + return *this; |
| 303 | + }; |
| 304 | + _CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign& operator=(__mb_copy_assign&&) = default; |
| 305 | +}; |
| 306 | + |
| 307 | +template <class _Tp, bool = movable<_Tp>> |
| 308 | +struct __mb_move_assign : __mb_copy_assign<_Tp> |
| 309 | +{ |
| 310 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_move_assign, __mb_copy_assign, _Tp); |
| 311 | +}; |
| 312 | + |
| 313 | +template <class _Tp> |
| 314 | +struct __mb_move_assign<_Tp, false> : __mb_copy_assign<_Tp> |
| 315 | +{ |
| 316 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_move_assign, __mb_copy_assign, _Tp); |
| 317 | + |
| 318 | + _CCCL_HIDE_FROM_ABI constexpr __mb_move_assign(const __mb_move_assign&) = default; |
| 319 | + _CCCL_HIDE_FROM_ABI constexpr __mb_move_assign(__mb_move_assign&&) = default; |
| 320 | + _CCCL_HIDE_FROM_ABI constexpr __mb_move_assign& operator=(const __mb_move_assign&) = default; |
| 321 | + |
| 322 | + _LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX20 __mb_move_assign& |
| 323 | + operator=(__mb_move_assign&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>) |
| 324 | + { |
| 325 | + static_assert(is_nothrow_move_constructible_v<_Tp>); |
| 326 | + static_assert(!__can_use_no_unique_address<_Tp>); |
| 327 | + if (this != _CUDA_VSTD::addressof(__other)) |
| 328 | + { |
| 329 | + _CUDA_VSTD::__destroy_at(_CUDA_VSTD::addressof(this->__holder_.__val_)); |
| 330 | + _CUDA_VSTD::__construct_at( |
| 331 | + _CUDA_VSTD::addressof(this->__holder_.__val_), _CUDA_VSTD::move(__other.__holder_.__val_)); |
| 332 | + } |
| 333 | + return *this; |
| 334 | + } |
| 335 | +}; |
| 336 | + |
| 337 | +template <class _Tp> |
| 338 | +struct __mb_base : __mb_move_assign<_Tp> |
| 339 | +{ |
| 340 | + _LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_base, __mb_move_assign, _Tp); |
| 341 | + |
| 342 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept |
| 343 | + { |
| 344 | + return this->__holder_.__val_; |
| 345 | + } |
| 346 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept |
| 347 | + { |
| 348 | + return this->__holder_.__val_; |
| 349 | + } |
| 350 | + |
| 351 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept |
| 352 | + { |
| 353 | + return _CUDA_VSTD::addressof(this->__holder_.__val_); |
| 354 | + } |
| 355 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept |
| 356 | + { |
| 357 | + return _CUDA_VSTD::addressof(this->__holder_.__val_); |
| 358 | + } |
| 359 | + |
| 360 | + _CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr bool __has_value() const noexcept |
| 361 | + { |
| 362 | + return true; |
| 363 | + } |
| 364 | +}; |
| 365 | + |
| 366 | +template <class _Tp> |
| 367 | +using __movable_box_base = _If<__doesnt_need_empty_state<_Tp>, __mb_base<_Tp>, __mb_optional_base<_Tp>>; |
| 368 | + |
| 369 | +// Primary template - uses _CUDA_VSTD::optional and introduces an empty state in case assignment fails. |
| 370 | +template <class _Tp, bool = __movable_box_object<_Tp>> |
| 371 | +struct __movable_box; |
| 372 | + |
| 373 | +template <class _Tp> |
| 374 | +struct __movable_box<_Tp, true> : __movable_box_base<_Tp> |
| 375 | +{ |
| 376 | + using __base = __movable_box_base<_Tp>; |
| 377 | + |
| 378 | + _CCCL_TEMPLATE(class... _Args) |
| 379 | + _CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>) |
| 380 | + _LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept( |
| 381 | + is_nothrow_constructible_v<_Tp, _Args...>) |
| 382 | + : __base(in_place, _CUDA_VSTD::forward<_Args>(__args)...) |
| 383 | + {} |
| 384 | + |
| 385 | + _CCCL_HIDE_FROM_ABI constexpr __movable_box() = default; |
| 386 | +}; |
| 387 | + |
| 388 | +_LIBCUDACXX_END_NAMESPACE_RANGES |
| 389 | + |
| 390 | +#endif // _LIBCUDACXX___RANGES_MOVABLE_BOX_H |
0 commit comments