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