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