Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <optional>
7 : #include <type_traits>
8 : #include <utility>
9 :
10 : #include "Utilities/Gsl.hpp"
11 :
12 : /// An object or reference.
13 : ///
14 : /// This class can either store an object or point to a preexisting
15 : /// one, giving uniform access to the two cases.
16 : template <typename T>
17 1 : class MaybeOwned {
18 : public:
19 0 : using value_type = T;
20 :
21 0 : MaybeOwned(MaybeOwned&&) = default;
22 0 : MaybeOwned& operator=(MaybeOwned&&) = default;
23 :
24 : // Not clear what correct semantics for copying a non-owning object
25 : // would be.
26 0 : MaybeOwned(const MaybeOwned&) = delete;
27 0 : MaybeOwned& operator=(const MaybeOwned&) = delete;
28 :
29 0 : ~MaybeOwned() = default;
30 :
31 : /// A default constructed MaybeOwned<T> owns a default-constructed T.
32 1 : MaybeOwned() : owned_value_(std::in_place) {}
33 :
34 : /// Create an owning object.
35 : // NOLINTNEXTLINE(google-explicit-constructor)
36 1 : MaybeOwned(std::remove_const_t<T> owned) : owned_value_(std::move(owned)) {}
37 : /// Create a non-owning object.
38 : // NOLINTNEXTLINE(google-explicit-constructor)
39 1 : MaybeOwned(const gsl::not_null<T*> unowned) : unowned_value_(unowned.get()) {}
40 :
41 : // The conversion sequence
42 : // not_null<T*> -> not_null<const T*> -> MaybeOwned<const T>
43 : // is too complex to be found automatically.
44 :
45 : /// Create a non-owning object when the previous constructor needs
46 : /// too many implicit conversions.
47 : // NOLINTNEXTLINE(google-explicit-constructor)
48 1 : MaybeOwned(const gsl::not_null<std::remove_const_t<T>*> unowned)
49 : requires std::is_const_v<T>
50 : : MaybeOwned(gsl::not_null<T*>(unowned)) {}
51 :
52 : /// Conversion from MaybeOwned<T> to MaybeOwned<const T>
53 : // NOLINTNEXTLINE(google-explicit-constructor)
54 1 : MaybeOwned(MaybeOwned<std::remove_const_t<T>>&& other)
55 : requires std::is_const_v<T>
56 : : owned_value_(std::move(other).owned_value_),
57 : unowned_value_(other.unowned_value_) {}
58 :
59 : /// Conversion from MaybeOwned<T> to MaybeOwned<const T>
60 1 : MaybeOwned& operator=(MaybeOwned<std::remove_const_t<T>>&& other)
61 : requires std::is_const_v<T>
62 : {
63 : owned_value_ = std::move(other).owned_value_;
64 : unowned_value_ = other.unowned_value_;
65 : return *this;
66 : }
67 :
68 : /// Change the object to owning, destroying any previously owned value.
69 1 : MaybeOwned& operator=(std::remove_const_t<T> owned) {
70 : owned_value_.emplace(std::move(owned));
71 : unowned_value_ = nullptr;
72 : return *this;
73 : }
74 : /// Change the object to non-owning.
75 1 : MaybeOwned& operator=(const gsl::not_null<T*> unowned) {
76 : owned_value_.reset();
77 : unowned_value_ = unowned.get();
78 : return *this;
79 : }
80 :
81 : /// Access the contained or referenced object
82 : /// @{
83 1 : T& operator*() {
84 : return owned_value_.has_value() ? *owned_value_ : *unowned_value_;
85 : }
86 1 : const T& operator*() const {
87 : return owned_value_.has_value() ? *owned_value_ : *unowned_value_;
88 : }
89 :
90 1 : T* operator->() { return &**this; }
91 1 : const T* operator->() const { return &**this; }
92 : /// @}
93 :
94 0 : bool is_owning() const { return owned_value_.has_value(); }
95 :
96 : /// Get the owned object, or a copy if non-owning.
97 0 : std::remove_const_t<T> to_owned() && {
98 : if (owned_value_.has_value()) {
99 : return std::move(*owned_value_);
100 : } else {
101 : return *unowned_value_;
102 : }
103 : }
104 :
105 : private:
106 : friend class MaybeOwned<const T>;
107 :
108 0 : std::optional<std::remove_const_t<T>> owned_value_{};
109 0 : T* unowned_value_ = nullptr;
110 : };
111 :
112 : template <typename T>
113 0 : MaybeOwned(T) -> MaybeOwned<T>;
114 :
115 : // Storing a reference or pointer to a not_null seems unlikely enough
116 : // that we assume the user wanted the other behavior. The class can
117 : // still be instantiated with an explicit argument if someone really
118 : // wants that.
119 : template <typename T>
120 0 : MaybeOwned(gsl::not_null<T*>) -> MaybeOwned<T>;
121 :
122 : // Disable pointers, as the user probably meant to use the not_null
123 : // call, but not definitely enough that inferring seems like a good
124 : // idea.
125 : //
126 : // "= delete" would be more appropriate, but the standards committee
127 : // appears to have approved that and then just forgotten to add it to
128 : // the standard or something.
129 : template <typename T>
130 0 : MaybeOwned(T*) -> MaybeOwned<void>;
|