UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
Optional.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
8#include "Misc/OptionalFwd.h"
10#include "Templates/TypeHash.h" // Definitions of GetTypeHash for primitive types must be visible when GetTypeHash for TOptional is defined
13#include <type_traits>
14
15inline constexpr FNullOpt NullOpt{0};
16
17namespace UE::Core::Private
18{
19 // Empty structure to replace bIsSet for intrusive optional types
20 struct FEmpty {};
21
22 // Shared code for optionals to handle multiple implementations of IsSet depending on compiler support for constrained destructors
23 // This class can be a friend of FIntrusiveUnsetOptionalState rather than all possible base classes.
24 struct FOptional
25 {
26 template<typename Derived>
27 [[nodiscard]] inline static constexpr bool IsSet(Derived* This)
28 {
29 if constexpr (Derived::bUsingIntrusiveUnsetState)
30 {
31 return !(This->TypedValue == FIntrusiveUnsetOptionalState{});
32 }
33 else
34 {
35 return This->bIsSet;
36 }
37 }
38 };
39
40#if !PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
41 // These base types are necessary for trivial destruction on compilers that don't yet support use of
42 // constraints to select an explicitly defaulted trivial destructor.
43 // Once such compilers are no longer supported, all (!PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS)
44 // code in this file can be deleted.
45 template <typename OptionalType, bool bIsTriviallyDestructible = std::is_trivially_destructible_v<OptionalType>>
47 {
48 union
49 {
51 };
52 // Set flag takes up no space if bIsUsingIntrusiveUnsetState
54 UE_NO_UNIQUE_ADDRESS std::conditional_t<bUsingIntrusiveUnsetState, FEmpty, bool> bIsSet = {};
55
56 constexpr TOptionalBase()
57 {
58 }
59
60 template<typename... ArgTypes>
61 constexpr TOptionalBase(EInPlace, ArgTypes&&... InArgs)
62 : TypedValue(Forward<ArgTypes>(InArgs)...)
63 {
64 }
65
66 // Explicitly constexpr destructor for trivially destructible OptionalType
67 constexpr ~TOptionalBase() = default;
68
69 [[nodiscard]] constexpr bool IsSet() const
70 {
71 return FOptional::IsSet(this);
72 }
73
75 {
76 // Type is known to be trivially destructible
77 }
78 };
79
80 template <typename OptionalType>
82 {
83 union
84 {
86 };
87 // Set flag takes up no space if bIsUsingIntrusiveUnsetState
89 UE_NO_UNIQUE_ADDRESS std::conditional_t<bUsingIntrusiveUnsetState, FEmpty, bool> bIsSet = {};
90
91 constexpr TOptionalBase()
92 {
93 }
94
95 template<typename... ArgTypes>
96 constexpr TOptionalBase(EInPlace, ArgTypes&&... InArgs)
97 : TypedValue(Forward<ArgTypes>(InArgs)...)
98 {
99 }
100
101 constexpr ~TOptionalBase()
102 {
103 if (IsSet())
104 {
105 DestroyValue();
106 }
107 }
108
109 [[nodiscard]] constexpr bool IsSet() const
110 {
111 return FOptional::IsSet(this);
112 }
113
115 {
116 DestructItem(std::addressof(this->TypedValue));
117 }
118 };
119#endif
120}
121
126template<typename OptionalType>
129 : private UE::Core::Private::TOptionalBase<OptionalType>
130#endif
131{
132private:
133#if !PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
135#endif
136
138 friend FOptional;
139 static constexpr bool bUsingIntrusiveUnsetState = HasIntrusiveUnsetOptionalState<OptionalType>();
140
141public:
143
147 {
148 }
153 template <typename... ArgTypes>
154 [[nodiscard]] explicit constexpr TOptional(EInPlace, ArgTypes&&... Args)
155#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
156 : TypedValue(Forward<ArgTypes>(Args)...)
157#else
158 : Super(InPlace, Forward<ArgTypes>(Args)...)
159#endif
160 {
161 // If this fails to compile when trying to call TOptional(EInPlace, ...) with a non-public constructor,
162 // do not make TOptional a friend.
163 //
164 // Instead, prefer this pattern:
165 //
166 // class FMyType
167 // {
168 // private:
169 // struct FPrivateToken { explicit FPrivateToken() = default; };
170 //
171 // public:
172 // // This has an equivalent access level to a private constructor,
173 // // as only friends of FMyType will have access to FPrivateToken,
174 // // but the TOptional constructor can legally call it since it's public.
175 // explicit FMyType(FPrivateToken, int32 Int, float Real, const TCHAR* String);
176 // };
177 //
178 // // Won't compile if the caller doesn't have access to FMyType::FPrivateToken
179 // TOptional<FMyType> Opt(InPlace, FMyType::FPrivateToken{}, 5, 3.14f, TEXT("Banana"));
180 //
181
182 if constexpr (!bUsingIntrusiveUnsetState)
183 {
184 this->bIsSet = true;
185 }
186 else
187 {
188 // Ensure that a user doesn't emplace an unset state into the optional
189 checkf(IsSet(), TEXT("TOptional::TOptional(EInPlace, ...) - optionals should not be unset by emplacement"));
190 }
191 }
192
195 : TOptional()
196 {
197 }
198
200 [[nodiscard]] constexpr TOptional() requires bUsingIntrusiveUnsetState
201#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
203#else
205#endif
206 {
207 }
208
210 [[nodiscard]] constexpr TOptional() requires (!bUsingIntrusiveUnsetState)
211 {
212 }
213
214#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
215 constexpr ~TOptional() requires std::is_trivially_destructible_v<OptionalType> = default;
217 {
218 // Destroy value but do not reconstruct an empty intrusive optional in its place
219 if (IsSet())
220 {
221 DestroyValue();
222 }
223 }
224#else
225 constexpr ~TOptional() = default; // Defer to base type to handle trivial/non-trivial destructor
226#endif
227
230 : TOptional()
231 {
232 bool bLocalIsSet = Other.IsSet();
233 if constexpr (!bUsingIntrusiveUnsetState)
234 {
235 this->bIsSet = bLocalIsSet;
236 }
237 if (bLocalIsSet)
238 {
239 ::new((void*)std::addressof(this->TypedValue)) OptionalType(Other.TypedValue);
240 }
241 // Default constructor initialized unset intrusive optional if necessary
242 }
244 : TOptional()
245 {
246 bool bLocalIsSet = Other.IsSet();
247 if constexpr (!bUsingIntrusiveUnsetState)
248 {
249 this->bIsSet = bLocalIsSet;
250 }
251 if (bLocalIsSet)
252 {
253 ::new((void*)std::addressof(this->TypedValue)) OptionalType(MoveTempIfPossible(Other.TypedValue));
254 }
255 // Default constructor initialized unset intrusive optional if necessary
256 }
257
259 {
260 if (&Other != this)
261 {
262 if (Other.IsSet())
263 {
264 Emplace(Other.GetValue());
265 }
266 else
267 {
268 Reset();
269 }
270 }
271 return *this;
272 }
274 {
275 if (&Other != this)
276 {
277 if(Other.IsSet())
278 {
279 Emplace(MoveTempIfPossible(Other.GetValue()));
280 }
281 else
282 {
283 Reset();
284 }
285 }
286 return *this;
287 }
288
290 {
291 if (std::addressof(InValue) != std::addressof(this->TypedValue))
292 {
293 Emplace(InValue);
294 }
295 return *this;
296 }
298 {
299 if (std::addressof(InValue) != std::addressof(this->TypedValue))
300 {
302 }
303 return *this;
304 }
305
306 void Reset()
307 {
308 if (IsSet())
309 {
310 DestroyValue();
311 if constexpr (bUsingIntrusiveUnsetState)
312 {
313 ::new((void*)std::addressof(this->TypedValue)) OptionalType(FIntrusiveUnsetOptionalState{});
314 }
315 else
316 {
317 this->bIsSet = false;
318 }
319 }
320 }
321
322 template <typename... ArgsType>
323 OptionalType& Emplace(ArgsType&&... Args)
324 {
325 // Destroy the member in-place before replacing it - a bit nasty, but it'll work since we don't support exceptions
326 if constexpr (bUsingIntrusiveUnsetState)
327 {
328 DestroyValue();
329 }
330 else
331 {
332 if (IsSet())
333 {
334 DestroyValue();
335 }
336 }
337
338 // If this fails to compile when trying to call Emplace with a non-public constructor,
339 // do not make TOptional a friend.
340 //
341 // Instead, prefer this pattern:
342 //
343 // class FMyType
344 // {
345 // private:
346 // struct FPrivateToken { explicit FPrivateToken() = default; };
347 //
348 // public:
349 // // This has an equivalent access level to a private constructor,
350 // // as only friends of FMyType will have access to FPrivateToken,
351 // // but Emplace can legally call it since it's public.
352 // explicit FMyType(FPrivateToken, int32 Int, float Real, const TCHAR* String);
353 // };
354 //
355 // TOptional<FMyType> Opt:
356 //
357 // // Won't compile if the caller doesn't have access to FMyType::FPrivateToken
358 // Opt.Emplace(FMyType::FPrivateToken{}, 5, 3.14f, TEXT("Banana"));
359 //
360 OptionalType* Result = ::new((void*)std::addressof(this->TypedValue)) OptionalType(Forward<ArgsType>(Args)...);
361
362 if constexpr (!bUsingIntrusiveUnsetState)
363 {
364 this->bIsSet = true;
365 }
366 else
367 {
368 // Ensure that a user doesn't emplace an unset state into the optional
369 checkf(IsSet(), TEXT("TOptional::Emplace(...) - optionals should not be unset by an emplacement"));
370 }
371
372 return *Result;
373 }
374
375 [[nodiscard]] friend constexpr bool operator==(const TOptional& Lhs, const TOptional& Rhs)
376 {
377 bool bIsLhsSet = Lhs.IsSet();
378 bool bIsRhsSet = Rhs.IsSet();
379
380 if (bIsLhsSet != bIsRhsSet)
381 {
382 return false;
383 }
384 if (!bIsLhsSet) // both unset
385 {
386 return true;
387 }
388
389 return Lhs.TypedValue == Rhs.TypedValue;
390 }
391
392 [[nodiscard]] friend constexpr bool operator!=(const TOptional& Lhs, const TOptional& Rhs)
393 {
394 return !(Lhs == Rhs);
395 }
396
398 {
399 bool bOptionalIsSet = IsSet();
400 if (Ar.IsLoading())
401 {
402 bool bOptionalWasSaved = false;
403 Ar << bOptionalWasSaved;
405 {
406 if (!bOptionalIsSet)
407 {
408 Emplace();
409 }
410 Ar << GetValue();
411 }
412 else
413 {
414 Reset();
415 }
416 }
417 else
418 {
419 Ar << bOptionalIsSet;
420 if (bOptionalIsSet)
421 {
422 Ar << GetValue();
423 }
424 }
425 }
426
428#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
429 [[nodiscard]] constexpr bool IsSet() const
430 {
431 return FOptional::IsSet(this);
432 }
433#else
435#endif
436
437 [[nodiscard]] UE_FORCEINLINE_HINT explicit constexpr operator bool() const
438 {
439 return IsSet();
440 }
441
444 {
445 checkf(IsSet(), TEXT("It is an error to call GetValue() on an unset TOptional. Please either check IsSet() or use Get(DefaultValue) instead."));
446 return this->TypedValue;
447 }
448 [[nodiscard]] UE_FORCEINLINE_HINT constexpr const OptionalType& GetValue() const
449 {
450 return const_cast<TOptional*>(this)->GetValue();
451 }
452
454 {
455 return std::addressof(GetValue());
456 }
458 {
459 return const_cast<TOptional*>(this)->operator->();
460 }
461
463 {
464 return GetValue();
465 }
467 {
468 return const_cast<TOptional*>(this)->operator*();
469 }
470
472 [[nodiscard]] constexpr const OptionalType& Get(const OptionalType& DefaultValue UE_LIFETIMEBOUND) const UE_LIFETIMEBOUND
473 {
474 return IsSet() ? this->TypedValue : DefaultValue;
475 }
476
479 {
480 return IsSet() ? std::addressof(this->TypedValue) : nullptr;
481 }
482 [[nodiscard]] UE_FORCEINLINE_HINT constexpr const OptionalType* GetPtrOrNull() const
483 {
484 return const_cast<TOptional*>(this)->GetPtrOrNull();
485 }
486
487private:
491#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
492 UE_FORCEINLINE_HINT constexpr void DestroyValue()
493 {
494 DestructItem(std::addressof(this->TypedValue));
495 }
496#else
498#endif
499
500 // If we can have an explicitly conditional trivial destructor, we can declare our members inline and avoid the base class
501#if PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
502 union
503 {
504 OptionalType TypedValue;
505 };
506 // Set flag takes up no space if bIsUsingIntrusiveUnsetState, otherwise is zero-initialized to false
507 UE_NO_UNIQUE_ADDRESS std::conditional_t<bUsingIntrusiveUnsetState, UE::Core::Private::FEmpty, bool> bIsSet = {};
508#endif
509};
510
511template<typename OptionalType>
517
518template<typename OptionalType>
520{
521 return Optional.IsSet() ? GetTypeHash(*Optional) : 0;
522}
523
527template <typename T> static constexpr bool TIsTOptional_V = false;
528template <typename T> static constexpr bool TIsTOptional_V< TOptional<T>> = true;
529template <typename T> static constexpr bool TIsTOptional_V<const TOptional<T>> = true;
530template <typename T> static constexpr bool TIsTOptional_V< volatile TOptional<T>> = true;
531template <typename T> static constexpr bool TIsTOptional_V<const volatile TOptional<T>> = true;
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
EInPlace
Definition CoreMiscDefines.h:162
@ InPlace
Definition CoreMiscDefines.h:162
#define PLATFORM_COMPILER_SUPPORTS_CONSTRAINED_DESTRUCTORS
Definition Platform.h:290
#define UE_LIFETIMEBOUND
Definition Platform.h:812
#define TEXT(x)
Definition Platform.h:1272
#define UE_NO_UNIQUE_ADDRESS
Definition Platform.h:799
#define UE_FORCEINLINE_HINT
Definition Platform.h:723
FArchive & operator<<(FArchive &Ar, TOptional< OptionalType > &Optional)
Definition Optional.h:512
auto GetTypeHash(const TOptional< OptionalType > &Optional) -> decltype(GetTypeHash(*Optional))
Definition Optional.h:519
constexpr FNullOpt NullOpt
Definition Optional.h:15
FORCEINLINE constexpr void DestructItem(ElementType *Element)
Definition MemoryOps.h:56
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
const bool
Definition NetworkReplayStreaming.h:178
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTempIfPossible(T &&Obj) noexcept
Definition UnrealTemplate.h:538
if(Failed) console_printf("Failed.\n")
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
UE_FORCEINLINE_HINT bool IsLoading() const
Definition Archive.h:236
implementation
Definition PlayInEditorLoadingScope.h:8
@ false
Definition radaudio_common.h:23
Definition IntrusiveUnsetOptionalState.h:71
Definition OptionalFwd.h:13
Definition Optional.h:131
constexpr TOptional()
Definition Optional.h:200
TOptional & operator=(const OptionalType &InValue)
Definition Optional.h:289
constexpr ~TOptional()=default
void Serialize(FArchive &Ar)
Definition Optional.h:397
constexpr TOptional(OptionalType &&InValue)
Definition Optional.h:149
constexpr TOptional(FNullOpt)
Definition Optional.h:194
constexpr OptionalType & GetValue()
Definition Optional.h:443
UE_FORCEINLINE_HINT constexpr const OptionalType * GetPtrOrNull() const
Definition Optional.h:482
UE_FORCEINLINE_HINT constexpr const OptionalType & operator*() const
Definition Optional.h:466
UE_FORCEINLINE_HINT constexpr const OptionalType & GetValue() const
Definition Optional.h:448
friend constexpr bool operator!=(const TOptional &Lhs, const TOptional &Rhs)
Definition Optional.h:392
TOptional & operator=(OptionalType &&InValue)
Definition Optional.h:297
constexpr bool IsSet() const
Definition Optional.h:69
constexpr OptionalType * GetPtrOrNull()
Definition Optional.h:478
friend constexpr bool operator==(const TOptional &Lhs, const TOptional &Rhs)
Definition Optional.h:375
void Reset()
Definition Optional.h:306
TOptional(TOptional &&Other)
Definition Optional.h:243
OptionalType & Emplace(ArgsType &&... Args)
Definition Optional.h:323
TOptional & operator=(const TOptional &Other)
Definition Optional.h:258
constexpr OptionalType & operator*()
Definition Optional.h:462
TOptional(const TOptional &Other)
Definition Optional.h:229
UE_FORCEINLINE_HINT constexpr const OptionalType * operator->() const
Definition Optional.h:457
OptionalType ElementType
Definition Optional.h:142
constexpr TOptional()
Definition Optional.h:210
constexpr TOptional(const OptionalType &InValue)
Definition Optional.h:145
constexpr TOptional(EInPlace, ArgTypes &&... Args)
Definition Optional.h:154
constexpr OptionalType * operator->()
Definition Optional.h:453
constexpr const OptionalType & Get(const OptionalType &DefaultValue UE_LIFETIMEBOUND) const UE_LIFETIMEBOUND
Definition Optional.h:472
TOptional & operator=(TOptional &&Other)
Definition Optional.h:273
Definition Optional.h:20
Definition Optional.h:25
static constexpr bool IsSet(Derived *This)
Definition Optional.h:27
constexpr bool IsSet() const
Definition Optional.h:109
constexpr TOptionalBase(EInPlace, ArgTypes &&... InArgs)
Definition Optional.h:96
constexpr TOptionalBase()
Definition Optional.h:91
constexpr ~TOptionalBase()
Definition Optional.h:101
OptionalType TypedValue
Definition Optional.h:85
UE_FORCEINLINE_HINT constexpr void DestroyValue()
Definition Optional.h:114
Definition Optional.h:47
UE_NO_UNIQUE_ADDRESS std::conditional_t< bUsingIntrusiveUnsetState, FEmpty, bool > bIsSet
Definition Optional.h:54
constexpr bool IsSet() const
Definition Optional.h:69
UE_FORCEINLINE_HINT constexpr void DestroyValue()
Definition Optional.h:74
constexpr TOptionalBase(EInPlace, ArgTypes &&... InArgs)
Definition Optional.h:61
OptionalType TypedValue
Definition Optional.h:50
static constexpr bool bUsingIntrusiveUnsetState
Definition Optional.h:53
constexpr ~TOptionalBase()=default
constexpr TOptionalBase()
Definition Optional.h:56