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