1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // UNSUPPORTED: c++03, c++11, c++14 |
10 | // <optional> |
11 | |
12 | // constexpr optional<T>& operator=(optional<T>&& rhs) |
13 | // noexcept(is_nothrow_move_assignable<T>::value && |
14 | // is_nothrow_move_constructible<T>::value); |
15 | |
16 | #include <optional> |
17 | #include <cassert> |
18 | #include <type_traits> |
19 | #include <utility> |
20 | |
21 | #include "test_macros.h" |
22 | #include "archetypes.h" |
23 | |
24 | using std::optional; |
25 | |
26 | struct X |
27 | { |
28 | static bool throw_now; |
29 | static int alive; |
30 | |
31 | X() { ++alive; } |
32 | X(X&&) |
33 | { |
34 | if (throw_now) |
35 | TEST_THROW(6); |
36 | ++alive; |
37 | } |
38 | |
39 | X& operator=(X&&) |
40 | { |
41 | if (throw_now) |
42 | TEST_THROW(42); |
43 | return *this; |
44 | } |
45 | |
46 | ~X() { assert(alive > 0); --alive; } |
47 | }; |
48 | |
49 | struct Y {}; |
50 | |
51 | bool X::throw_now = false; |
52 | int X::alive = 0; |
53 | |
54 | |
55 | template <class Tp> |
56 | constexpr bool assign_empty(optional<Tp>&& lhs) { |
57 | optional<Tp> rhs; |
58 | lhs = std::move(rhs); |
59 | return !lhs.has_value() && !rhs.has_value(); |
60 | } |
61 | |
62 | template <class Tp> |
63 | constexpr bool assign_value(optional<Tp>&& lhs) { |
64 | optional<Tp> rhs(101); |
65 | lhs = std::move(rhs); |
66 | return lhs.has_value() && rhs.has_value() && *lhs == Tp{101}; |
67 | } |
68 | |
69 | int main(int, char**) |
70 | { |
71 | { |
72 | static_assert(std::is_nothrow_move_assignable<optional<int>>::value, "" ); |
73 | optional<int> opt; |
74 | constexpr optional<int> opt2; |
75 | opt = std::move(opt2); |
76 | static_assert(static_cast<bool>(opt2) == false, "" ); |
77 | assert(static_cast<bool>(opt) == static_cast<bool>(opt2)); |
78 | } |
79 | { |
80 | optional<int> opt; |
81 | constexpr optional<int> opt2(2); |
82 | opt = std::move(opt2); |
83 | static_assert(static_cast<bool>(opt2) == true, "" ); |
84 | static_assert(*opt2 == 2, "" ); |
85 | assert(static_cast<bool>(opt) == static_cast<bool>(opt2)); |
86 | assert(*opt == *opt2); |
87 | } |
88 | { |
89 | optional<int> opt(3); |
90 | constexpr optional<int> opt2; |
91 | opt = std::move(opt2); |
92 | static_assert(static_cast<bool>(opt2) == false, "" ); |
93 | assert(static_cast<bool>(opt) == static_cast<bool>(opt2)); |
94 | } |
95 | { |
96 | using T = TestTypes::TestType; |
97 | T::reset(); |
98 | optional<T> opt(3); |
99 | optional<T> opt2; |
100 | assert(T::alive == 1); |
101 | opt = std::move(opt2); |
102 | assert(T::alive == 0); |
103 | assert(static_cast<bool>(opt2) == false); |
104 | assert(static_cast<bool>(opt) == static_cast<bool>(opt2)); |
105 | } |
106 | { |
107 | optional<int> opt(3); |
108 | constexpr optional<int> opt2(2); |
109 | opt = std::move(opt2); |
110 | static_assert(static_cast<bool>(opt2) == true, "" ); |
111 | static_assert(*opt2 == 2, "" ); |
112 | assert(static_cast<bool>(opt) == static_cast<bool>(opt2)); |
113 | assert(*opt == *opt2); |
114 | } |
115 | { |
116 | using O = optional<int>; |
117 | static_assert(assign_empty(lhs: O{42})); |
118 | static_assert(assign_value(lhs: O{42})); |
119 | assert(assign_empty(O{42})); |
120 | assert(assign_value(O{42})); |
121 | } |
122 | { |
123 | using O = optional<TrivialTestTypes::TestType>; |
124 | static_assert(assign_empty(O{42})); |
125 | static_assert(assign_value(O{42})); |
126 | assert(assign_empty(O{42})); |
127 | assert(assign_value(O{42})); |
128 | } |
129 | #ifndef TEST_HAS_NO_EXCEPTIONS |
130 | { |
131 | static_assert(!std::is_nothrow_move_assignable<optional<X>>::value, "" ); |
132 | X::alive = 0; |
133 | X::throw_now = false; |
134 | optional<X> opt; |
135 | optional<X> opt2(X{}); |
136 | assert(X::alive == 1); |
137 | assert(static_cast<bool>(opt2) == true); |
138 | try |
139 | { |
140 | X::throw_now = true; |
141 | opt = std::move(opt2); |
142 | assert(false); |
143 | } |
144 | catch (int i) |
145 | { |
146 | assert(i == 6); |
147 | assert(static_cast<bool>(opt) == false); |
148 | } |
149 | assert(X::alive == 1); |
150 | } |
151 | assert(X::alive == 0); |
152 | { |
153 | static_assert(!std::is_nothrow_move_assignable<optional<X>>::value, "" ); |
154 | X::throw_now = false; |
155 | optional<X> opt(X{}); |
156 | optional<X> opt2(X{}); |
157 | assert(X::alive == 2); |
158 | assert(static_cast<bool>(opt2) == true); |
159 | try |
160 | { |
161 | X::throw_now = true; |
162 | opt = std::move(opt2); |
163 | assert(false); |
164 | } |
165 | catch (int i) |
166 | { |
167 | assert(i == 42); |
168 | assert(static_cast<bool>(opt) == true); |
169 | } |
170 | assert(X::alive == 2); |
171 | } |
172 | assert(X::alive == 0); |
173 | #endif // TEST_HAS_NO_EXCEPTIONS |
174 | { |
175 | static_assert(std::is_nothrow_move_assignable<optional<Y>>::value, "" ); |
176 | } |
177 | { |
178 | struct ThrowsMove { |
179 | ThrowsMove() noexcept {} |
180 | ThrowsMove(ThrowsMove const&) noexcept {} |
181 | ThrowsMove(ThrowsMove &&) noexcept(false) {} |
182 | ThrowsMove& operator=(ThrowsMove const&) noexcept { return *this; } |
183 | ThrowsMove& operator=(ThrowsMove &&) noexcept { return *this; } |
184 | }; |
185 | static_assert(!std::is_nothrow_move_assignable<optional<ThrowsMove>>::value, "" ); |
186 | struct ThrowsMoveAssign { |
187 | ThrowsMoveAssign() noexcept {} |
188 | ThrowsMoveAssign(ThrowsMoveAssign const&) noexcept {} |
189 | ThrowsMoveAssign(ThrowsMoveAssign &&) noexcept {} |
190 | ThrowsMoveAssign& operator=(ThrowsMoveAssign const&) noexcept { return *this; } |
191 | ThrowsMoveAssign& operator=(ThrowsMoveAssign &&) noexcept(false) { return *this; } |
192 | }; |
193 | static_assert(!std::is_nothrow_move_assignable<optional<ThrowsMoveAssign>>::value, "" ); |
194 | struct NoThrowMove { |
195 | NoThrowMove() noexcept(false) {} |
196 | NoThrowMove(NoThrowMove const&) noexcept(false) {} |
197 | NoThrowMove(NoThrowMove &&) noexcept {} |
198 | NoThrowMove& operator=(NoThrowMove const&) noexcept { return *this; } |
199 | NoThrowMove& operator=(NoThrowMove&&) noexcept { return *this; } |
200 | }; |
201 | static_assert(std::is_nothrow_move_assignable<optional<NoThrowMove>>::value, "" ); |
202 | } |
203 | return 0; |
204 | } |
205 | |