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 11 // Throwing bad_variant_access is supported starting in macosx10.13 12 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions 13 14 // <variant> 15 16 // template <class ...Types> class variant; 17 18 // variant(variant&&) noexcept(see below); // constexpr in C++20 19 20 #include <cassert> 21 #include <string> 22 #include <type_traits> 23 #include <variant> 24 25 #include "test_macros.h" 26 #include "test_workarounds.h" 27 28 struct ThrowsMove { 29 ThrowsMove(ThrowsMove &&) noexcept(false) {} 30 }; 31 32 struct NoCopy { 33 NoCopy(const NoCopy &) = delete; 34 }; 35 36 struct MoveOnly { 37 int value; 38 MoveOnly(int v) : value(v) {} 39 MoveOnly(const MoveOnly &) = delete; 40 MoveOnly(MoveOnly &&) = default; 41 }; 42 43 struct MoveOnlyNT { 44 int value; 45 MoveOnlyNT(int v) : value(v) {} 46 MoveOnlyNT(const MoveOnlyNT &) = delete; 47 MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } 48 }; 49 50 struct NTMove { 51 constexpr NTMove(int v) : value(v) {} 52 NTMove(const NTMove &) = delete; 53 NTMove(NTMove &&that) : value(that.value) { that.value = -1; } 54 int value; 55 }; 56 57 static_assert(!std::is_trivially_move_constructible<NTMove>::value, ""); 58 static_assert(std::is_move_constructible<NTMove>::value, ""); 59 60 struct TMove { 61 constexpr TMove(int v) : value(v) {} 62 TMove(const TMove &) = delete; 63 TMove(TMove &&) = default; 64 int value; 65 }; 66 67 static_assert(std::is_trivially_move_constructible<TMove>::value, ""); 68 69 struct TMoveNTCopy { 70 constexpr TMoveNTCopy(int v) : value(v) {} 71 TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {} 72 TMoveNTCopy(TMoveNTCopy&&) = default; 73 int value; 74 }; 75 76 static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, ""); 77 78 #ifndef TEST_HAS_NO_EXCEPTIONS 79 struct MakeEmptyT { 80 static int alive; 81 MakeEmptyT() { ++alive; } 82 MakeEmptyT(const MakeEmptyT &) { 83 ++alive; 84 // Don't throw from the copy constructor since variant's assignment 85 // operator performs a copy before committing to the assignment. 86 } 87 MakeEmptyT(MakeEmptyT &&) { throw 42; } 88 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } 89 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } 90 ~MakeEmptyT() { --alive; } 91 }; 92 93 int MakeEmptyT::alive = 0; 94 95 template <class Variant> void makeEmpty(Variant &v) { 96 Variant v2(std::in_place_type<MakeEmptyT>); 97 try { 98 v = std::move(v2); 99 assert(false); 100 } catch (...) { 101 assert(v.valueless_by_exception()); 102 } 103 } 104 #endif // TEST_HAS_NO_EXCEPTIONS 105 106 void test_move_noexcept() { 107 { 108 using V = std::variant<int, long>; 109 static_assert(std::is_nothrow_move_constructible<V>::value, ""); 110 } 111 { 112 using V = std::variant<int, MoveOnly>; 113 static_assert(std::is_nothrow_move_constructible<V>::value, ""); 114 } 115 { 116 using V = std::variant<int, MoveOnlyNT>; 117 static_assert(!std::is_nothrow_move_constructible<V>::value, ""); 118 } 119 { 120 using V = std::variant<int, ThrowsMove>; 121 static_assert(!std::is_nothrow_move_constructible<V>::value, ""); 122 } 123 } 124 125 void test_move_ctor_sfinae() { 126 { 127 using V = std::variant<int, long>; 128 static_assert(std::is_move_constructible<V>::value, ""); 129 } 130 { 131 using V = std::variant<int, MoveOnly>; 132 static_assert(std::is_move_constructible<V>::value, ""); 133 } 134 { 135 using V = std::variant<int, MoveOnlyNT>; 136 static_assert(std::is_move_constructible<V>::value, ""); 137 } 138 { 139 using V = std::variant<int, NoCopy>; 140 static_assert(!std::is_move_constructible<V>::value, ""); 141 } 142 143 // Make sure we properly propagate triviality (see P0602R4). 144 #if TEST_STD_VER > 17 145 { 146 using V = std::variant<int, long>; 147 static_assert(std::is_trivially_move_constructible<V>::value, ""); 148 } 149 { 150 using V = std::variant<int, NTMove>; 151 static_assert(!std::is_trivially_move_constructible<V>::value, ""); 152 static_assert(std::is_move_constructible<V>::value, ""); 153 } 154 { 155 using V = std::variant<int, TMove>; 156 static_assert(std::is_trivially_move_constructible<V>::value, ""); 157 } 158 { 159 using V = std::variant<int, TMoveNTCopy>; 160 static_assert(std::is_trivially_move_constructible<V>::value, ""); 161 } 162 #endif // > C++17 163 } 164 165 template <typename T> 166 struct Result { size_t index; T value; }; 167 168 void test_move_ctor_basic() { 169 { 170 std::variant<int> v(std::in_place_index<0>, 42); 171 std::variant<int> v2 = std::move(v); 172 assert(v2.index() == 0); 173 assert(std::get<0>(v2) == 42); 174 } 175 { 176 std::variant<int, long> v(std::in_place_index<1>, 42); 177 std::variant<int, long> v2 = std::move(v); 178 assert(v2.index() == 1); 179 assert(std::get<1>(v2) == 42); 180 } 181 { 182 std::variant<MoveOnly> v(std::in_place_index<0>, 42); 183 assert(v.index() == 0); 184 std::variant<MoveOnly> v2(std::move(v)); 185 assert(v2.index() == 0); 186 assert(std::get<0>(v2).value == 42); 187 } 188 { 189 std::variant<int, MoveOnly> v(std::in_place_index<1>, 42); 190 assert(v.index() == 1); 191 std::variant<int, MoveOnly> v2(std::move(v)); 192 assert(v2.index() == 1); 193 assert(std::get<1>(v2).value == 42); 194 } 195 { 196 std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42); 197 assert(v.index() == 0); 198 std::variant<MoveOnlyNT> v2(std::move(v)); 199 assert(v2.index() == 0); 200 assert(std::get<0>(v).value == -1); 201 assert(std::get<0>(v2).value == 42); 202 } 203 { 204 std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42); 205 assert(v.index() == 1); 206 std::variant<int, MoveOnlyNT> v2(std::move(v)); 207 assert(v2.index() == 1); 208 assert(std::get<1>(v).value == -1); 209 assert(std::get<1>(v2).value == 42); 210 } 211 212 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 213 #if TEST_STD_VER > 17 214 { 215 struct { 216 constexpr Result<int> operator()() const { 217 std::variant<int> v(std::in_place_index<0>, 42); 218 std::variant<int> v2 = std::move(v); 219 return {v2.index(), std::get<0>(std::move(v2))}; 220 } 221 } test; 222 constexpr auto result = test(); 223 static_assert(result.index == 0, ""); 224 static_assert(result.value == 42, ""); 225 } 226 { 227 struct { 228 constexpr Result<long> operator()() const { 229 std::variant<int, long> v(std::in_place_index<1>, 42); 230 std::variant<int, long> v2 = std::move(v); 231 return {v2.index(), std::get<1>(std::move(v2))}; 232 } 233 } test; 234 constexpr auto result = test(); 235 static_assert(result.index == 1, ""); 236 static_assert(result.value == 42, ""); 237 } 238 { 239 struct { 240 constexpr Result<TMove> operator()() const { 241 std::variant<TMove> v(std::in_place_index<0>, 42); 242 std::variant<TMove> v2(std::move(v)); 243 return {v2.index(), std::get<0>(std::move(v2))}; 244 } 245 } test; 246 constexpr auto result = test(); 247 static_assert(result.index == 0, ""); 248 static_assert(result.value.value == 42, ""); 249 } 250 { 251 struct { 252 constexpr Result<TMove> operator()() const { 253 std::variant<int, TMove> v(std::in_place_index<1>, 42); 254 std::variant<int, TMove> v2(std::move(v)); 255 return {v2.index(), std::get<1>(std::move(v2))}; 256 } 257 } test; 258 constexpr auto result = test(); 259 static_assert(result.index == 1, ""); 260 static_assert(result.value.value == 42, ""); 261 } 262 { 263 struct { 264 constexpr Result<TMoveNTCopy> operator()() const { 265 std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42); 266 std::variant<TMoveNTCopy> v2(std::move(v)); 267 return {v2.index(), std::get<0>(std::move(v2))}; 268 } 269 } test; 270 constexpr auto result = test(); 271 static_assert(result.index == 0, ""); 272 static_assert(result.value.value == 42, ""); 273 } 274 { 275 struct { 276 constexpr Result<TMoveNTCopy> operator()() const { 277 std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42); 278 std::variant<int, TMoveNTCopy> v2(std::move(v)); 279 return {v2.index(), std::get<1>(std::move(v2))}; 280 } 281 } test; 282 constexpr auto result = test(); 283 static_assert(result.index == 1, ""); 284 static_assert(result.value.value == 42, ""); 285 } 286 #endif // > C++17 287 } 288 289 void test_move_ctor_valueless_by_exception() { 290 #ifndef TEST_HAS_NO_EXCEPTIONS 291 using V = std::variant<int, MakeEmptyT>; 292 V v1; 293 makeEmpty(v1); 294 V v(std::move(v1)); 295 assert(v.valueless_by_exception()); 296 #endif // TEST_HAS_NO_EXCEPTIONS 297 } 298 299 template <size_t Idx> 300 constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) { 301 auto copy = v; 302 auto v2 = std::move(copy); 303 return v2.index() == v.index() && 304 v2.index() == Idx && 305 std::get<Idx>(v2) == std::get<Idx>(v); 306 } 307 308 void test_constexpr_move_ctor() { 309 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 310 #if TEST_STD_VER > 17 311 using V = std::variant<long, void*, const int>; 312 #ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE 313 static_assert(std::is_trivially_destructible<V>::value, ""); 314 static_assert(std::is_trivially_copy_constructible<V>::value, ""); 315 static_assert(std::is_trivially_move_constructible<V>::value, ""); 316 static_assert(!std::is_copy_assignable<V>::value, ""); 317 static_assert(!std::is_move_assignable<V>::value, ""); 318 #else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE 319 static_assert(std::is_trivially_copyable<V>::value, ""); 320 #endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE 321 static_assert(std::is_trivially_move_constructible<V>::value, ""); 322 static_assert(test_constexpr_ctor_imp<0>(V(42l)), ""); 323 static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), ""); 324 static_assert(test_constexpr_ctor_imp<2>(V(101)), ""); 325 #endif // > C++17 326 } 327 328 int main(int, char**) { 329 test_move_ctor_basic(); 330 test_move_ctor_valueless_by_exception(); 331 test_move_noexcept(); 332 test_move_ctor_sfinae(); 333 test_constexpr_move_ctor(); 334 335 return 0; 336 } 337