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 <optional>
7#include <type_traits>
8
9#include <boost/optional/optional_fwd.hpp>
10
11#include <userver/utils/assert.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils {
16
17/// @ingroup userver_universal userver_containers
18///
19/// @brief Class that behaves as a nullable reference. Main difference from the
20/// pointer - value comparison of pointed values.
21///
22/// Initializes from reference to a T or from optional<T>.
23///
24/// Once the reference is constructed it can not be changed to point to
25/// different address.
26template <class T>
28public:
29 using value_type = T;
30
31 static_assert(!std::is_reference<T>::value, "Do not use a reference for T");
32
33 constexpr OptionalRef() noexcept = default;
34 constexpr OptionalRef(std::nullopt_t) noexcept {}
35 constexpr OptionalRef(const OptionalRef&) noexcept = default;
36 constexpr OptionalRef& operator=(const OptionalRef&) noexcept = delete;
37
38 constexpr OptionalRef(T& other) noexcept : data_(&other) {}
39
40 // Forming a reference to a temporary is forbidden
41 explicit constexpr OptionalRef(const T&&) = delete;
42
43 template <typename U>
44 explicit constexpr OptionalRef(const std::optional<U>& other) noexcept : data_(GetPointer(other)) {}
45
46 template <typename U>
47 explicit constexpr OptionalRef(std::optional<U>& other) noexcept : data_(GetPointer(other)) {}
48
49 template <typename U>
50 explicit constexpr OptionalRef(const std::optional<U>&&) noexcept {
51 static_assert(!sizeof(U), "Forming a reference to a temporary");
52 }
53
54 template <typename U>
55 explicit constexpr OptionalRef(const boost::optional<U>& other) noexcept : data_(GetPointer(other)) {}
56
57 template <typename U>
58 explicit constexpr OptionalRef(boost::optional<U>& other) noexcept : data_(GetPointer(other)) {}
59
60 template <typename U>
61 explicit constexpr OptionalRef(const boost::optional<U>&&) noexcept {
62 static_assert(!sizeof(U), "Forming a reference to a temporary");
63 }
64
65 constexpr bool has_value() const noexcept { return !!data_; }
66 constexpr explicit operator bool() const noexcept { return has_value(); }
67
68 constexpr T* operator->() const {
69 UASSERT(data_);
70 return data_;
71 }
72
73 constexpr T& operator*() const {
74 UASSERT(data_);
75 return *data_;
76 }
77
78 constexpr T& value() const {
79 if (!has_value()) {
80 throw std::bad_optional_access();
81 }
82
83 return *data_;
84 }
85
86 template <typename U>
87 constexpr T value_or(U&& default_value) const {
88 if (!has_value()) {
89 return std::forward<U>(default_value);
90 }
91
92 return *data_;
93 }
94
95private:
96 template <class Optional>
97 static T* GetPointer(Optional& other) noexcept {
98 using ValueType = decltype(*other);
99 static_assert(
100 std::is_const<T>::value || !std::is_const<ValueType>::value,
101 "Attempt to initialize non-const T from a const optional value"
102 );
103
104 if (!other) {
105 return nullptr;
106 }
107
108 auto& value = *other;
109 return &value;
110 }
111
112 T* const data_ = nullptr;
113};
114
115template <class T, class U>
116constexpr bool operator==(OptionalRef<T> lhs, OptionalRef<U> rhs) noexcept {
117 if (!lhs || !rhs) {
118 return !lhs && !rhs;
119 }
120 return *lhs == *rhs;
121}
122
123template <class T, class U>
124constexpr bool operator!=(OptionalRef<T> lhs, OptionalRef<U> rhs) noexcept {
125 return !(lhs == rhs);
126}
127
128} // namespace utils
129
130USERVER_NAMESPACE_END