Skip to content

Commit 2e6625f

Browse files
authored
Update unsafe_optional.hpp
1 parent 7ea5e3d commit 2e6625f

File tree

1 file changed

+72
-46
lines changed

1 file changed

+72
-46
lines changed

β€Žunsafe_optional.hpp

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
synopsis
55
66
template<class T, class = void>
7-
union unsafe_optional_imp; // for internal use
7+
union unsafe_optional_data; // for internal use
8+
9+
template<class T>
10+
class unsafe_optional_impl; // for internal use
811
912
template<class T>
1013
class unsafe_optional;
@@ -28,8 +31,8 @@
2831
// Constructors
2932
constexpr unsafe_optional() noexcept;
3033
constexpr unsafe_optional(std::nullopt_t) noexcept;
31-
unsafe_optional(const unsafe_optional&) = delete;
32-
unsafe_optional(unsafe_optional&&); // always throws
34+
unsafe_optional(const unsafe_optional&);
35+
unsafe_optional(unsafe_optional&&);
3336
constexpr unsafe_optional(const T&);
3437
constexpr unsafe_optional(T&&);
3538
template<class... Args>
@@ -43,8 +46,8 @@
4346
~unsafe_optional();
4447
4548
// Assignment
46-
unsafe_optional& operator=(const unsafe_optional&) = delete;
47-
unsafe_optional& operator=(unsafe_optional&&) = delete;
49+
unsafe_optional& operator=(const unsafe_optional&);
50+
unsafe_optional& operator=(unsafe_optional&&);
4851
template<class... Args> void emplace(Args&&...);
4952
template<class U, class... Args>
5053
void emplace(std::initializer_list<U>, Args&&...);
@@ -64,40 +67,44 @@
6467
#include <optional>
6568
#include <stdexcept>
6669
#include <type_traits>
70+
#include "smf_control.hpp"
6771

6872
template<class T>
6973
class unsafe_optional;
7074

75+
template<class T>
76+
class unsafe_optional_impl;
77+
7178
template<class T, class = void>
72-
union unsafe_optional_imp {
73-
friend unsafe_optional<T>;
79+
union unsafe_optional_data {
80+
friend unsafe_optional_impl<T>;
7481
private:
7582
unsigned char uninitialized;
7683
T val;
7784
private:
78-
constexpr unsafe_optional_imp() noexcept : uninitialized() {}
85+
constexpr unsafe_optional_data() noexcept : uninitialized() {}
7986
template<class... Args>
80-
constexpr explicit unsafe_optional_imp(std::in_place_t, Args&&... args)
87+
constexpr explicit unsafe_optional_data(std::in_place_t, Args&&... args)
8188
: val(std::forward<Args>(args)...) {}
82-
~unsafe_optional_imp() = default;
89+
~unsafe_optional_data() = default;
8390
};
8491

8592
// The partial specialization is the same as the primary template except
8693
// that the destructor is explicitly defined.
8794
template<class T>
88-
union unsafe_optional_imp<T,
89-
std::enable_if_t<!std::is_trivially_destructible<T>{}>
95+
union unsafe_optional_data<T,
96+
std::enable_if_t<!std::is_trivially_destructible<T>{}()>
9097
> {
9198
friend unsafe_optional<T>;
9299
private:
93100
unsigned char uninitialized;
94101
T val;
95102
private:
96-
constexpr unsafe_optional_imp() noexcept : uninitialized() {}
103+
constexpr unsafe_optional_data() noexcept : uninitialized() {}
97104
template<class... Args>
98-
constexpr explicit unsafe_optional_imp(std::in_place_t, Args&&... args)
105+
constexpr explicit unsafe_optional_data(std::in_place_t, Args&&... args)
99106
: val(std::forward<Args>(args)...) {}
100-
~unsafe_optional_imp() {}
107+
~unsafe_optional_data() {}
101108
};
102109

103110
template<class T>
@@ -110,57 +117,52 @@ struct is_unsafe_optional<unsafe_optional<T>> : std::true_type {};
110117
// Compared to std::optional, unsafe_optional lacks observer functions
111118
// such like has_value/operator bool, and it lacks value_or,
112119
// comparison operators, and specialization of std::hash.
113-
// unsafe_optional<T> is not copy constructible.
114-
// unsafe_optional<T> is move constructible but is not MoveConstructible.
115-
// This is because its move constructor always throws.
116-
// Move a unsafe_optional<T> only when copy elision is guaranteed to happen.
117-
// unsafe_optional<T> is not copy/move assignable.
120+
// unsafe_optional<T> is not copy constructible unless T is trivially
121+
// copy constructible.
122+
// unsafe_optional<T> is move constructible, but its move constructor always
123+
// throws unless T is trivially move constructble.
124+
// Move constructing unsafe_optional<T> will throw an exception, unless
125+
// T is trivially move constructble or the move construction is elided.
126+
// unsafe_optional<T> is not copy/move assignable unless T is trivially
127+
// copy/move assignable.
118128
// Overloaded operators such as operator*, operator-> and operator=
119129
// are not provided; instead, all functions are named, to avoid accidental use.
120130
// Use a unsafe_optional only when the initialization state is known outside
121-
// or the use only involves constant expressions.
131+
// or when the use only involves constant expressions.
122132
template<class T>
123-
class unsafe_optional {
124-
static_assert(sizeof(unsafe_optional_imp<T>) == sizeof(T),
125-
"The wrapper has unintended space overhead.");
126-
static_assert(alignof(unsafe_optional_imp<T>) == alignof(T),
127-
"The wrapper has a different alignment from the wrapped type.");
128-
static_assert(!std::is_reference<T>{}, "T cannot be a reference type.");
129-
// unsafe_optional<unsafe_optional> is quite useless.
130-
static_assert(!is_unsafe_optional<std::remove_cv_t<T>>{},
131-
"T cannot be a unsafe_optional.");
132-
// unsafe_optional<std::in_place_t> and unsafe_optional<std::in_place_t>
133-
// may be tricky to use. They might be usable though....
134-
private:
135-
unsafe_optional_imp<T> data;
133+
class unsafe_optional_impl {
134+
unsafe_optional_data<T> data;
136135
public:
137136
using value_type = T;
138137

139-
constexpr unsafe_optional() noexcept = default;
140-
constexpr unsafe_optional(std::nullopt_t) noexcept : unsafe_optional() {}
141-
unsafe_optional(const unsafe_optional&) = delete;
142-
[[noreturn]] unsafe_optional(unsafe_optional&&) {
138+
constexpr unsafe_optional_impl() noexcept = default;
139+
constexpr unsafe_optional_impl(std::nullopt_t) noexcept
140+
: unsafe_optional_impl() {}
141+
unsafe_optional_impl(const unsafe_optional_impl&) = default;
142+
unsafe_optional_impl(unsafe_optional_impl&&) = default;
143+
[[noreturn]]
144+
unsafe_optional_impl(user_provided_tag, unsafe_optional_impl&&) {
143145
throw std::runtime_error("unsafe_optional move constructor called");
144146
}
145-
constexpr unsafe_optional(const T& v) : data(std::in_place, v) {}
146-
constexpr unsafe_optional(T&& v) : data(std::in_place, std::move(v)) {}
147+
constexpr unsafe_optional_impl(const T& v) : data(std::in_place, v) {}
148+
constexpr unsafe_optional_impl(T&& v) : data(std::in_place, std::move(v)) {}
147149
template<class... Args>
148-
constexpr explicit unsafe_optional(std::in_place_t, Args&&... args)
150+
constexpr explicit unsafe_optional_impl(std::in_place_t, Args&&... args)
149151
: data(std::in_place, std::forward<Args>(args)...) {}
150152
template <class U, class... Args,
151153
std::enable_if_t<
152-
std::is_constructible<T,std::initializer_list<U>&,Args&&...>{},
154+
std::is_constructible<T,std::initializer_list<U>&,Args&&...>{}(),
153155
int> = 0
154156
>
155-
constexpr explicit unsafe_optional(
157+
constexpr explicit unsafe_optional_impl(
156158
std::in_place_t, std::initializer_list<U> il, Args&&... args
157159
)
158160
: data(std::in_place, il, std::forward<Args>(args)...) {}
159161

160-
~unsafe_optional() = default;
162+
~unsafe_optional_impl() = default;
161163

162-
unsafe_optional& operator=(unsafe_optional const&) = delete;
163-
unsafe_optional& operator=(unsafe_optional&&) = delete;
164+
unsafe_optional_impl& operator=(unsafe_optional_impl const&) = default;
165+
unsafe_optional_impl& operator=(unsafe_optional_impl&&) = default;
164166

165167
template<class... Args>
166168
void emplace(Args&&... args) {
@@ -183,6 +185,30 @@ class unsafe_optional {
183185
void reset() noexcept { data.val.~T(); }
184186
};
185187

188+
template<class T>
189+
class unsafe_optional : public
190+
delete_copy_ctor_if<!std::is_trivially_copy_constructible<T>{}(),
191+
default_move_ctor_if<std::is_trivially_copy_constructible<T>{}(),
192+
delete_copy_assign_if<!std::is_trivially_copy_assignable<T>{}(),
193+
delete_move_assign_if<!std::is_trivially_move_assignable<T>{}(),
194+
unsafe_optional_impl<T>
195+
>>>> {
196+
static_assert(sizeof(unsafe_optional_data<T>) == sizeof(T),
197+
"The wrapper has unintended space overhead.");
198+
static_assert(alignof(unsafe_optional_data<T>) == alignof(T),
199+
"The wrapper has a different alignment from the wrapped type.");
200+
static_assert(!std::is_reference<T>{}(), "T cannot be a reference type.");
201+
// unsafe_optional_impl<unsafe_optional_impl> is quite useless.
202+
static_assert(!is_unsafe_optional<std::remove_cv_t<T>>{}(),
203+
"T cannot be a unsafe_optional_impl.");
204+
// unsafe_optional<std::in_place_t> and unsafe_optional<std::nullopt_t>
205+
// are not disabled, but they may be a bit tricky to use.
206+
207+
using base = typename unsafe_optional::delete_copy_ctor_if;
208+
public:
209+
using base::base;
210+
};
211+
186212
template<class T>
187213
constexpr unsafe_optional<std::decay_t<T>> make_unsafe_optional(T&& v) {
188214
return unsafe_optional<std::decay_t<T>>(std::forward<T>(v));

0 commit comments

Comments
 (0)