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