4
4
synopsis
5
5
6
6
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
8
11
9
12
template<class T>
10
13
class unsafe_optional;
28
31
// Constructors
29
32
constexpr unsafe_optional() noexcept;
30
33
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&&);
33
36
constexpr unsafe_optional(const T&);
34
37
constexpr unsafe_optional(T&&);
35
38
template<class... Args>
43
46
~unsafe_optional();
44
47
45
48
// 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&&);
48
51
template<class... Args> void emplace(Args&&...);
49
52
template<class U, class... Args>
50
53
void emplace(std::initializer_list<U>, Args&&...);
64
67
#include < optional>
65
68
#include < stdexcept>
66
69
#include < type_traits>
70
+ #include " smf_control.hpp"
67
71
68
72
template <class T >
69
73
class unsafe_optional ;
70
74
75
+ template <class T >
76
+ class unsafe_optional_impl ;
77
+
71
78
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>;
74
81
private:
75
82
unsigned char uninitialized;
76
83
T val;
77
84
private:
78
- constexpr unsafe_optional_imp () noexcept : uninitialized () {}
85
+ constexpr unsafe_optional_data () noexcept : uninitialized () {}
79
86
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)
81
88
: val (std::forward<Args>(args)...) {}
82
- ~unsafe_optional_imp () = default ;
89
+ ~unsafe_optional_data () = default ;
83
90
};
84
91
85
92
// The partial specialization is the same as the primary template except
86
93
// that the destructor is explicitly defined.
87
94
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>{}() >
90
97
> {
91
98
friend unsafe_optional<T>;
92
99
private:
93
100
unsigned char uninitialized;
94
101
T val;
95
102
private:
96
- constexpr unsafe_optional_imp () noexcept : uninitialized () {}
103
+ constexpr unsafe_optional_data () noexcept : uninitialized () {}
97
104
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)
99
106
: val (std::forward<Args>(args)...) {}
100
- ~unsafe_optional_imp () {}
107
+ ~unsafe_optional_data () {}
101
108
};
102
109
103
110
template <class T >
@@ -110,57 +117,52 @@ struct is_unsafe_optional<unsafe_optional<T>> : std::true_type {};
110
117
// Compared to std::optional, unsafe_optional lacks observer functions
111
118
// such like has_value/operator bool, and it lacks value_or,
112
119
// 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.
118
128
// Overloaded operators such as operator*, operator-> and operator=
119
129
// are not provided; instead, all functions are named, to avoid accidental use.
120
130
// 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.
122
132
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;
136
135
public:
137
136
using value_type = T;
138
137
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&&) {
143
145
throw std::runtime_error (" unsafe_optional move constructor called" );
144
146
}
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)) {}
147
149
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)
149
151
: data(std::in_place, std::forward<Args>(args)...) {}
150
152
template <class U , class ... Args,
151
153
std::enable_if_t <
152
- std::is_constructible<T,std::initializer_list<U>&,Args&&...>{},
154
+ std::is_constructible<T,std::initializer_list<U>&,Args&&...>{}() ,
153
155
int > = 0
154
156
>
155
- constexpr explicit unsafe_optional (
157
+ constexpr explicit unsafe_optional_impl (
156
158
std::in_place_t , std::initializer_list<U> il, Args&&... args
157
159
)
158
160
: data(std::in_place, il, std::forward<Args>(args)...) {}
159
161
160
- ~unsafe_optional () = default ;
162
+ ~unsafe_optional_impl () = default ;
161
163
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 ;
164
166
165
167
template <class ... Args>
166
168
void emplace (Args&&... args) {
@@ -183,6 +185,30 @@ class unsafe_optional {
183
185
void reset () noexcept { data.val .~T (); }
184
186
};
185
187
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
+
186
212
template <class T >
187
213
constexpr unsafe_optional<std::decay_t <T>> make_unsafe_optional (T&& v) {
188
214
return unsafe_optional<std::decay_t <T>>(std::forward<T>(v));
0 commit comments