userver: userver/utils/optional_ref.hpp Source File
Loading...
Searching...
No Matches
optional_ref.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/optional_ref.hpp
4/// @brief @copybrief utils::OptionalRef
5
6#include <functional>
7#include <memory>
8#include <optional>
9#include <type_traits>
10#include <utility>
11
12#include <boost/optional/optional_fwd.hpp>
13
14#include <userver/utils/assert.hpp>
15#include <userver/utils/meta_light.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace utils {
20
21/// @ingroup userver_universal userver_containers
22///
23/// @brief Class that behaves as a nullable reference. Main difference from the
24/// pointer - value comparison of pointed values.
25///
26/// Initializes from reference to a T or from optional<T>.
27///
28/// Once the reference is constructed it can not be changed to point to
29/// different address.
30template <class T>
32public:
33 using value_type = T;
34
35 static_assert(!std::is_reference<T>::value, "Do not use a reference for T");
36
37 constexpr OptionalRef() noexcept = default;
38 constexpr OptionalRef(std::nullopt_t) noexcept {}
39 constexpr OptionalRef(const OptionalRef&) noexcept = default;
40 constexpr OptionalRef& operator=(const OptionalRef&) noexcept = delete;
41
42 constexpr OptionalRef(T& other) noexcept : data_(std::addressof(other)) {}
43
44 // Forming a reference to a temporary is forbidden
45 constexpr explicit OptionalRef(const T&&) = delete;
46
47 template <typename U>
48 constexpr explicit OptionalRef(const std::optional<U>& other) noexcept : data_(GetPointer(other)) {}
49
50 template <typename U>
51 constexpr explicit OptionalRef(std::optional<U>& other) noexcept : data_(GetPointer(other)) {}
52
53 template <typename U>
54 constexpr explicit OptionalRef(const std::optional<U>&&) noexcept {
55 static_assert(!sizeof(U), "Forming a reference to a temporary");
56 }
57
58 template <typename U>
59 constexpr explicit OptionalRef(const boost::optional<U>& other) noexcept : data_(GetPointer(other)) {}
60
61 template <typename U>
62 constexpr explicit OptionalRef(boost::optional<U>& other) noexcept : data_(GetPointer(other)) {}
63
64 template <typename U>
65 constexpr explicit OptionalRef(const boost::optional<U>&&) noexcept {
66 static_assert(!sizeof(U), "Forming a reference to a temporary");
67 }
68
69 constexpr bool has_value() const noexcept { return !!data_; }
70 constexpr explicit operator bool() const noexcept { return has_value(); }
71
72 constexpr T* operator->() const {
73 UASSERT(data_);
74 return data_;
75 }
76
77 constexpr T& operator*() const {
78 UASSERT(data_);
79 return *data_;
80 }
81
82 constexpr T& value() const {
83 if (!has_value()) {
84 throw std::bad_optional_access();
85 }
86
87 return *data_;
88 }
89
90 template <typename U>
91 constexpr T value_or(U&& default_value) const {
92 if (!has_value()) {
93 return std::forward<U>(default_value);
94 }
95
96 return *data_;
97 }
98
99 template <typename F>
100 constexpr auto and_then(F&& function) const -> std::remove_cvref_t<std::invoke_result_t<F, T&>> {
101 using Result = std::remove_cvref_t<std::invoke_result_t<F, T&>>;
102 static_assert(
103 meta::IsInstantiationOf<Result, std::optional> || meta::IsInstantiationOf<Result, OptionalRef>,
104 "The function passed to and_then must return std::optional or utils::OptionalRef"
105 );
106
107 if (has_value()) {
108 return std::invoke(std::forward<F>(function), *data_);
109 }
110 return Result{};
111 }
112
113 template <typename F>
114 constexpr auto transform(F&& function) const {
115 using Result = std::remove_cv_t<std::invoke_result_t<F, T&>>;
116
117 if (has_value()) {
118 return std::optional<Result>{std::invoke(std::forward<F>(function), *data_)};
119 }
120 return std::optional<Result>{};
121 }
122
123 template <typename F>
124 constexpr OptionalRef or_else(F&& function) const {
125 using Result = std::remove_cvref_t<std::invoke_result_t<F>>;
126 static_assert(
127 std::is_same_v<Result, OptionalRef>,
128 "The function passed to or_else must return utils::OptionalRef<T>"
129 );
130
131 if (has_value()) {
132 return *this;
133 }
134 return std::invoke(std::forward<F>(function));
135 }
136
137private:
138 template <class Optional>
139 static T* GetPointer(Optional& other) noexcept {
140 using ValueType = decltype(*other);
141 static_assert(
142 std::is_const<T>::value || !std::is_const<ValueType>::value,
143 "Attempt to initialize non-const T from a const optional value"
144 );
145
146 return other.has_value() ? std::addressof(*other) : nullptr;
147 }
148
149 T* const data_ = nullptr;
150};
151
152template <class T, class U>
153constexpr bool operator==(OptionalRef<T> lhs, OptionalRef<U> rhs) noexcept {
154 if (!lhs || !rhs) {
155 return !lhs && !rhs;
156 }
157 return *lhs == *rhs;
158}
159
160} // namespace utils
161
162USERVER_NAMESPACE_END