Skip to content

Commit 76ca355

Browse files
authored
Allow constexpr optionals. (#1509)
Extracted from #1490 as it's unclear when that change will land.
1 parent 18859ec commit 76ca355

File tree

2 files changed

+129
-84
lines changed

2 files changed

+129
-84
lines changed

include/vcpkg/base/optional.h

Lines changed: 125 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -23,205 +23,246 @@ namespace vcpkg
2323

2424
namespace details
2525
{
26+
struct EngageTag
27+
{
28+
};
29+
30+
template<class T, bool IsTrivallyDestructible = std::is_trivially_destructible_v<T>>
31+
struct OptionalStorageDtor
32+
{
33+
bool m_is_present;
34+
union
35+
{
36+
char m_inactive;
37+
T m_t;
38+
};
39+
40+
constexpr OptionalStorageDtor() : m_is_present(false), m_inactive() { }
41+
template<class... Args>
42+
constexpr OptionalStorageDtor(EngageTag,
43+
Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
44+
: m_is_present(true), m_t(std::forward<Args>(args)...)
45+
{
46+
}
47+
};
48+
49+
template<class T>
50+
struct OptionalStorageDtor<T, false>
51+
{
52+
bool m_is_present;
53+
union
54+
{
55+
char m_inactive;
56+
T m_t;
57+
};
58+
59+
constexpr OptionalStorageDtor() : m_is_present(false), m_inactive() { }
60+
template<class... Args>
61+
constexpr OptionalStorageDtor(EngageTag,
62+
Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
63+
: m_is_present(true), m_t(std::forward<Args>(args)...)
64+
{
65+
}
66+
67+
~OptionalStorageDtor()
68+
{
69+
if (m_is_present)
70+
{
71+
m_t.~T();
72+
}
73+
}
74+
};
75+
2676
template<class T, bool B = std::is_copy_constructible_v<T>>
27-
struct OptionalStorage
77+
struct OptionalStorage : OptionalStorageDtor<T>
2878
{
29-
constexpr OptionalStorage() noexcept : m_is_present(false), m_inactive() { }
79+
OptionalStorage() = default;
3080
constexpr OptionalStorage(const T& t) noexcept(std::is_nothrow_copy_constructible_v<T>)
31-
: m_is_present(true), m_t(t)
81+
: OptionalStorageDtor<T>(EngageTag{}, t)
3282
{
3383
}
3484
constexpr OptionalStorage(T&& t) noexcept(std::is_nothrow_move_constructible_v<T>)
35-
: m_is_present(true), m_t(std::move(t))
85+
: OptionalStorageDtor<T>(EngageTag{}, std::move(t))
3686
{
3787
}
3888
template<class U, class = std::enable_if_t<!std::is_reference_v<U>>>
3989
explicit OptionalStorage(Optional<U>&& t) noexcept(std::is_nothrow_constructible_v<T, U>)
40-
: m_is_present(false), m_inactive()
90+
: OptionalStorageDtor<T>()
4191
{
4292
if (auto p = t.get())
4393
{
44-
m_is_present = true;
45-
new (&m_t) T(std::move(*p));
94+
this->m_is_present = true;
95+
new (&this->m_t) T(std::move(*p));
4696
}
4797
}
4898
template<class U>
4999
explicit OptionalStorage(const Optional<U>& t) noexcept(std::is_nothrow_constructible_v<T, const U&>)
50-
: m_is_present(false), m_inactive()
100+
: OptionalStorageDtor<T>()
51101
{
52102
if (auto p = t.get())
53103
{
54-
m_is_present = true;
55-
new (&m_t) T(*p);
104+
this->m_is_present = true;
105+
new (&this->m_t) T(*p);
56106
}
57107
}
58108

59-
~OptionalStorage()
60-
{
61-
if (m_is_present) m_t.~T();
62-
}
63-
64109
OptionalStorage(const OptionalStorage& o) noexcept(std::is_nothrow_copy_constructible_v<T>)
65-
: m_is_present(o.m_is_present), m_inactive()
110+
: OptionalStorageDtor<T>()
66111
{
67-
if (m_is_present) new (&m_t) T(o.m_t);
112+
if (o.m_is_present)
113+
{
114+
this->m_is_present = true;
115+
new (&this->m_t) T(o.m_t);
116+
}
68117
}
69118

70119
OptionalStorage(OptionalStorage&& o) noexcept(std::is_nothrow_move_constructible_v<T>)
71-
: m_is_present(o.m_is_present), m_inactive()
120+
: OptionalStorageDtor<T>()
72121
{
73-
if (m_is_present)
122+
if (o.m_is_present)
74123
{
75-
new (&m_t) T(std::move(o.m_t));
124+
this->m_is_present = true;
125+
new (&this->m_t) T(std::move(o.m_t));
76126
}
77127
}
78128

79129
OptionalStorage& operator=(const OptionalStorage& o) noexcept(std::is_nothrow_copy_constructible_v<T> &&
80130
std::is_nothrow_copy_assignable_v<T>)
81131
{
82-
if (m_is_present && o.m_is_present)
132+
if (this->m_is_present && o.m_is_present)
83133
{
84-
m_t = o.m_t;
134+
this->m_t = o.m_t;
85135
}
86-
else if (!m_is_present && o.m_is_present)
136+
else if (!this->m_is_present && o.m_is_present)
87137
{
88-
new (&m_t) T(o.m_t);
89-
m_is_present = true;
138+
new (&this->m_t) T(o.m_t);
139+
this->m_is_present = true;
90140
}
91-
else if (m_is_present && !o.m_is_present)
141+
else if (this->m_is_present && !o.m_is_present)
92142
{
93143
destroy();
94144
}
145+
95146
return *this;
96147
}
97148

98149
OptionalStorage& operator=(OptionalStorage&& o) noexcept // enforces termination
99150
{
100-
if (m_is_present && o.m_is_present)
151+
if (this->m_is_present && o.m_is_present)
101152
{
102-
m_t = std::move(o.m_t);
153+
this->m_t = std::move(o.m_t);
103154
}
104-
else if (!m_is_present && o.m_is_present)
155+
else if (!this->m_is_present && o.m_is_present)
105156
{
106-
new (&m_t) T(std::move(o.m_t));
107-
m_is_present = true;
157+
new (&this->m_t) T(std::move(o.m_t));
158+
this->m_is_present = true;
108159
}
109-
else if (m_is_present && !o.m_is_present)
160+
else if (this->m_is_present && !o.m_is_present)
110161
{
111162
destroy();
112163
}
113164
return *this;
114165
}
115166

116-
constexpr bool has_value() const noexcept { return m_is_present; }
167+
constexpr bool has_value() const noexcept { return this->m_is_present; }
117168

118169
const T& value() const noexcept { return this->m_t; }
119170
T& value() noexcept { return this->m_t; }
120171

121-
const T* get() const& noexcept { return m_is_present ? &m_t : nullptr; }
122-
T* get() & noexcept { return m_is_present ? &m_t : nullptr; }
172+
const T* get() const& noexcept { return this->m_is_present ? &this->m_t : nullptr; }
173+
T* get() & noexcept { return this->m_is_present ? &this->m_t : nullptr; }
123174
const T* get() const&& = delete;
124175
T* get() && = delete;
125176

126177
void destroy() noexcept // enforces termination
127178
{
128-
m_is_present = false;
129-
m_t.~T();
130-
m_inactive = '\0';
179+
this->m_is_present = false;
180+
this->m_t.~T();
181+
this->m_inactive = '\0';
131182
}
132183

133184
template<class... Args>
134185
T& emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
135186
{
136-
if (m_is_present) destroy();
137-
new (&m_t) T(static_cast<Args&&>(args)...);
138-
m_is_present = true;
139-
return m_t;
187+
if (this->m_is_present) destroy();
188+
new (&this->m_t) T(static_cast<Args&&>(args)...);
189+
this->m_is_present = true;
190+
return this->m_t;
140191
}
141-
142-
private:
143-
bool m_is_present;
144-
union
145-
{
146-
char m_inactive;
147-
T m_t;
148-
};
149192
};
150193

151194
template<class T>
152-
struct OptionalStorage<T, false>
195+
struct OptionalStorage<T, false> : OptionalStorageDtor<T>
153196
{
154-
constexpr OptionalStorage() noexcept : m_is_present(false), m_inactive() { }
197+
OptionalStorage() = default;
155198
constexpr OptionalStorage(T&& t) noexcept(std::is_nothrow_move_constructible_v<T>)
156-
: m_is_present(true), m_t(std::move(t))
199+
: OptionalStorageDtor<T>(EngageTag{}, std::move(t))
157200
{
158201
}
159-
160-
~OptionalStorage()
202+
template<class U, class = std::enable_if_t<!std::is_reference_v<U>>>
203+
explicit OptionalStorage(Optional<U>&& t) noexcept(std::is_nothrow_constructible_v<T, U>)
204+
: OptionalStorageDtor<T>()
161205
{
162-
if (m_is_present) m_t.~T();
206+
if (auto p = t.get())
207+
{
208+
this->m_is_present = true;
209+
new (&this->m_t) T(std::move(*p));
210+
}
163211
}
164-
165212
OptionalStorage(OptionalStorage&& o) noexcept(std::is_nothrow_move_constructible_v<T>)
166-
: m_is_present(o.m_is_present), m_inactive()
213+
: OptionalStorageDtor<T>()
167214
{
168-
if (m_is_present)
215+
if (o.m_is_present)
169216
{
170-
new (&m_t) T(std::move(o.m_t));
217+
this->m_is_present = true;
218+
new (&this->m_t) T(std::move(o.m_t));
171219
}
172220
}
173221

174222
OptionalStorage& operator=(OptionalStorage&& o) noexcept // enforces termination
175223
{
176-
if (m_is_present && o.m_is_present)
224+
if (this->m_is_present && o.m_is_present)
177225
{
178-
m_t = std::move(o.m_t);
226+
this->m_t = std::move(o.m_t);
179227
}
180-
else if (!m_is_present && o.m_is_present)
228+
else if (!this->m_is_present && o.m_is_present)
181229
{
182-
m_is_present = true;
183-
new (&m_t) T(std::move(o.m_t));
230+
this->m_is_present = true;
231+
new (&this->m_t) T(std::move(o.m_t));
184232
}
185-
else if (m_is_present && !o.m_is_present)
233+
else if (this->m_is_present && !o.m_is_present)
186234
{
187235
destroy();
188236
}
237+
189238
return *this;
190239
}
191240

192-
constexpr bool has_value() const noexcept { return m_is_present; }
241+
constexpr bool has_value() const noexcept { return this->m_is_present; }
193242

194243
const T& value() const noexcept { return this->m_t; }
195244
T& value() noexcept { return this->m_t; }
196245

197-
const T* get() const& noexcept { return m_is_present ? &m_t : nullptr; }
198-
T* get() & noexcept { return m_is_present ? &m_t : nullptr; }
246+
const T* get() const& noexcept { return this->m_is_present ? &this->m_t : nullptr; }
247+
T* get() & noexcept { return this->m_is_present ? &this->m_t : nullptr; }
199248
const T* get() const&& = delete;
200249
T* get() && = delete;
201250

202251
template<class... Args>
203252
T& emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
204253
{
205-
if (m_is_present) destroy();
206-
new (&m_t) T(static_cast<Args&&>(args)...);
207-
m_is_present = true;
208-
return m_t;
254+
if (this->m_is_present) destroy();
255+
new (&this->m_t) T(static_cast<Args&&>(args)...);
256+
this->m_is_present = true;
257+
return this->m_t;
209258
}
210259

211260
void destroy() noexcept
212261
{
213-
m_is_present = false;
214-
m_t.~T();
215-
m_inactive = '\0';
262+
this->m_is_present = false;
263+
this->m_t.~T();
264+
this->m_inactive = '\0';
216265
}
217-
218-
private:
219-
bool m_is_present;
220-
union
221-
{
222-
char m_inactive;
223-
T m_t;
224-
};
225266
};
226267

227268
template<class T, bool B>

src/vcpkg-test/optional.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ TEST_CASE ("value conversion", "[optional]")
6565

6666
Optional<long> j = 1;
6767
Optional<int> i = j;
68+
(void)i;
6869
Optional<const char*> cstr = "hello, world!";
6970
Optional<std::string> cppstr = cstr;
7071

@@ -89,9 +90,12 @@ TEST_CASE ("optional.map", "[optional]")
8990
const Optional<std::unique_ptr<int>> move_only;
9091

9192
Optional<int*> m = move_only.map([](auto&& p) { return p.get(); });
93+
(void)m;
9294
Optional<Optional<int*>> n =
9395
move_only.map([](auto&& p) -> Optional<int*> { return p ? Optional<int*>{p.get()} : nullopt; });
96+
(void)n;
9497
Optional<NullOpt> o = move_only.map([](auto&&) { return nullopt; });
98+
(void)o;
9599

96100
Optional<int> five = 5;
97101

0 commit comments

Comments
 (0)