1// -*- C++ -*- 2//===----------------------------------------------------------------------===// 3// 4// The LLVM Compiler Infrastructure 5// 6// This file is dual licensed under the MIT and the University of Illinois Open 7// Source Licenses. See LICENSE.TXT for details. 8// 9//===----------------------------------------------------------------------===// 10 11// UNSUPPORTED: c++98, c++03, c++11, c++14 12 13// <variant> 14 15// template <class ...Types> class variant; 16 17// variant& operator=(variant&&) noexcept(see below); 18 19#include <cassert> 20#include <string> 21#include <type_traits> 22#include <utility> 23#include <variant> 24 25#include "test_macros.h" 26#include "variant_test_helpers.hpp" 27 28struct NoCopy { 29 NoCopy(const NoCopy &) = delete; 30 NoCopy &operator=(const NoCopy &) = default; 31}; 32 33struct CopyOnly { 34 CopyOnly(const CopyOnly &) = default; 35 CopyOnly(CopyOnly &&) = delete; 36 CopyOnly &operator=(const CopyOnly &) = default; 37 CopyOnly &operator=(CopyOnly &&) = delete; 38}; 39 40struct MoveOnly { 41 MoveOnly(const MoveOnly &) = delete; 42 MoveOnly(MoveOnly &&) = default; 43 MoveOnly &operator=(const MoveOnly &) = delete; 44 MoveOnly &operator=(MoveOnly &&) = default; 45}; 46 47struct MoveOnlyNT { 48 MoveOnlyNT(const MoveOnlyNT &) = delete; 49 MoveOnlyNT(MoveOnlyNT &&) {} 50 MoveOnlyNT &operator=(const MoveOnlyNT &) = delete; 51 MoveOnlyNT &operator=(MoveOnlyNT &&) = default; 52}; 53 54struct MoveOnlyOddNothrow { 55 MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} 56 MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete; 57 MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; 58 MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete; 59}; 60 61struct MoveAssignOnly { 62 MoveAssignOnly(MoveAssignOnly &&) = delete; 63 MoveAssignOnly &operator=(MoveAssignOnly &&) = default; 64}; 65 66struct MoveAssign { 67 static int move_construct; 68 static int move_assign; 69 static void reset() { move_construct = move_assign = 0; } 70 MoveAssign(int v) : value(v) {} 71 MoveAssign(MoveAssign &&o) : value(o.value) { 72 ++move_construct; 73 o.value = -1; 74 } 75 MoveAssign &operator=(MoveAssign &&o) { 76 value = o.value; 77 ++move_assign; 78 o.value = -1; 79 return *this; 80 } 81 int value; 82}; 83 84int MoveAssign::move_construct = 0; 85int MoveAssign::move_assign = 0; 86 87void test_move_assignment_noexcept() { 88 { 89 using V = std::variant<int>; 90 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 91 } 92 { 93 using V = std::variant<MoveOnly>; 94 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 95 } 96 { 97 using V = std::variant<int, long>; 98 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 99 } 100 { 101 using V = std::variant<int, MoveOnly>; 102 static_assert(std::is_nothrow_move_assignable<V>::value, ""); 103 } 104 { 105 using V = std::variant<MoveOnlyNT>; 106 static_assert(!std::is_nothrow_move_assignable<V>::value, ""); 107 } 108 { 109 using V = std::variant<MoveOnlyOddNothrow>; 110 static_assert(!std::is_nothrow_move_assignable<V>::value, ""); 111 } 112} 113 114void test_move_assignment_sfinae() { 115 { 116 using V = std::variant<int, long>; 117 static_assert(std::is_move_assignable<V>::value, ""); 118 } 119 { 120 // variant only provides move assignment when both the move constructor 121 // and move assignment operator are well formed. 122 using V = std::variant<int, CopyOnly>; 123 static_assert(!std::is_move_assignable<V>::value, ""); 124 } 125 { 126 using V = std::variant<int, NoCopy>; 127 static_assert(!std::is_move_assignable<V>::value, ""); 128 } 129 { 130 using V = std::variant<int, MoveOnly>; 131 static_assert(std::is_move_assignable<V>::value, ""); 132 } 133 { 134 using V = std::variant<int, MoveOnlyNT>; 135 static_assert(std::is_move_assignable<V>::value, ""); 136 } 137 { 138 // variant only provides move assignment when the types also provide 139 // a move constructor. 140 using V = std::variant<int, MoveAssignOnly>; 141 static_assert(!std::is_move_assignable<V>::value, ""); 142 } 143} 144 145void test_move_assignment_empty_empty() { 146#ifndef TEST_HAS_NO_EXCEPTIONS 147 using MET = MakeEmptyT; 148 { 149 using V = std::variant<int, long, MET>; 150 V v1(std::in_place_index<0>); 151 makeEmpty(v1); 152 V v2(std::in_place_index<0>); 153 makeEmpty(v2); 154 V &vref = (v1 = std::move(v2)); 155 assert(&vref == &v1); 156 assert(v1.valueless_by_exception()); 157 assert(v1.index() == std::variant_npos); 158 } 159#endif 160} 161 162void test_move_assignment_non_empty_empty() { 163#ifndef TEST_HAS_NO_EXCEPTIONS 164 using MET = MakeEmptyT; 165 { 166 using V = std::variant<int, MET>; 167 V v1(std::in_place_index<0>, 42); 168 V v2(std::in_place_index<0>); 169 makeEmpty(v2); 170 V &vref = (v1 = std::move(v2)); 171 assert(&vref == &v1); 172 assert(v1.valueless_by_exception()); 173 assert(v1.index() == std::variant_npos); 174 } 175 { 176 using V = std::variant<int, MET, std::string>; 177 V v1(std::in_place_index<2>, "hello"); 178 V v2(std::in_place_index<0>); 179 makeEmpty(v2); 180 V &vref = (v1 = std::move(v2)); 181 assert(&vref == &v1); 182 assert(v1.valueless_by_exception()); 183 assert(v1.index() == std::variant_npos); 184 } 185#endif 186} 187 188void test_move_assignment_empty_non_empty() { 189#ifndef TEST_HAS_NO_EXCEPTIONS 190 using MET = MakeEmptyT; 191 { 192 using V = std::variant<int, MET>; 193 V v1(std::in_place_index<0>); 194 makeEmpty(v1); 195 V v2(std::in_place_index<0>, 42); 196 V &vref = (v1 = std::move(v2)); 197 assert(&vref == &v1); 198 assert(v1.index() == 0); 199 assert(std::get<0>(v1) == 42); 200 } 201 { 202 using V = std::variant<int, MET, std::string>; 203 V v1(std::in_place_index<0>); 204 makeEmpty(v1); 205 V v2(std::in_place_type<std::string>, "hello"); 206 V &vref = (v1 = std::move(v2)); 207 assert(&vref == &v1); 208 assert(v1.index() == 2); 209 assert(std::get<2>(v1) == "hello"); 210 } 211#endif 212} 213 214void test_move_assignment_same_index() { 215 { 216 using V = std::variant<int>; 217 V v1(43); 218 V v2(42); 219 V &vref = (v1 = std::move(v2)); 220 assert(&vref == &v1); 221 assert(v1.index() == 0); 222 assert(std::get<0>(v1) == 42); 223 } 224 { 225 using V = std::variant<int, long, unsigned>; 226 V v1(43l); 227 V v2(42l); 228 V &vref = (v1 = std::move(v2)); 229 assert(&vref == &v1); 230 assert(v1.index() == 1); 231 assert(std::get<1>(v1) == 42); 232 } 233 { 234 using V = std::variant<int, MoveAssign, unsigned>; 235 V v1(std::in_place_type<MoveAssign>, 43); 236 V v2(std::in_place_type<MoveAssign>, 42); 237 MoveAssign::reset(); 238 V &vref = (v1 = std::move(v2)); 239 assert(&vref == &v1); 240 assert(v1.index() == 1); 241 assert(std::get<1>(v1).value == 42); 242 assert(MoveAssign::move_construct == 0); 243 assert(MoveAssign::move_assign == 1); 244 } 245#ifndef TEST_HAS_NO_EXCEPTIONS 246 using MET = MakeEmptyT; 247 { 248 using V = std::variant<int, MET, std::string>; 249 V v1(std::in_place_type<MET>); 250 MET &mref = std::get<1>(v1); 251 V v2(std::in_place_type<MET>); 252 try { 253 v1 = std::move(v2); 254 assert(false); 255 } catch (...) { 256 } 257 assert(v1.index() == 1); 258 assert(&std::get<1>(v1) == &mref); 259 } 260#endif 261} 262 263void test_move_assignment_different_index() { 264 { 265 using V = std::variant<int, long, unsigned>; 266 V v1(43); 267 V v2(42l); 268 V &vref = (v1 = std::move(v2)); 269 assert(&vref == &v1); 270 assert(v1.index() == 1); 271 assert(std::get<1>(v1) == 42); 272 } 273 { 274 using V = std::variant<int, MoveAssign, unsigned>; 275 V v1(std::in_place_type<unsigned>, 43); 276 V v2(std::in_place_type<MoveAssign>, 42); 277 MoveAssign::reset(); 278 V &vref = (v1 = std::move(v2)); 279 assert(&vref == &v1); 280 assert(v1.index() == 1); 281 assert(std::get<1>(v1).value == 42); 282 assert(MoveAssign::move_construct == 1); 283 assert(MoveAssign::move_assign == 0); 284 } 285#ifndef TEST_HAS_NO_EXCEPTIONS 286 using MET = MakeEmptyT; 287 { 288 using V = std::variant<int, MET, std::string>; 289 V v1(std::in_place_type<int>); 290 V v2(std::in_place_type<MET>); 291 try { 292 v1 = std::move(v2); 293 assert(false); 294 } catch (...) { 295 } 296 assert(v1.valueless_by_exception()); 297 assert(v1.index() == std::variant_npos); 298 } 299 { 300 using V = std::variant<int, MET, std::string>; 301 V v1(std::in_place_type<MET>); 302 V v2(std::in_place_type<std::string>, "hello"); 303 V &vref = (v1 = std::move(v2)); 304 assert(&vref == &v1); 305 assert(v1.index() == 2); 306 assert(std::get<2>(v1) == "hello"); 307 } 308#endif 309} 310 311int main() { 312 test_move_assignment_empty_empty(); 313 test_move_assignment_non_empty_empty(); 314 test_move_assignment_empty_non_empty(); 315 test_move_assignment_same_index(); 316 test_move_assignment_different_index(); 317 test_move_assignment_sfinae(); 318 test_move_assignment_noexcept(); 319} 320