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