xref: /llvm-project/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp (revision 06e2b737aa0347b42e8bf37cb00a053eab0a9393)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // <tuple>
10 
11 // template <class... Types> class tuple;
12 
13 // tuple& operator=(tuple&& u);
14 
15 // UNSUPPORTED: c++03
16 
17 #include <memory>
18 #include <tuple>
19 #include <utility>
20 #include <cassert>
21 
22 #include "test_macros.h"
23 #include "MoveOnly.h"
24 
25 struct NonAssignable {
26   NonAssignable& operator=(NonAssignable const&) = delete;
27   NonAssignable& operator=(NonAssignable&&) = delete;
28 };
29 struct CopyAssignable {
30   CopyAssignable& operator=(CopyAssignable const&) = default;
31   CopyAssignable& operator=(CopyAssignable&&) = delete;
32 };
33 static_assert(std::is_copy_assignable<CopyAssignable>::value, "");
34 struct MoveAssignable {
35   MoveAssignable& operator=(MoveAssignable const&) = delete;
36   MoveAssignable& operator=(MoveAssignable&&) = default;
37 };
38 struct NothrowMoveAssignable {
operator =NothrowMoveAssignable39   NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
40 };
41 struct PotentiallyThrowingMoveAssignable {
operator =PotentiallyThrowingMoveAssignable42   PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
43 };
44 
45 struct CountAssign {
46   static int copied;
47   static int moved;
resetCountAssign48   static void reset() { copied = moved = 0; }
49   CountAssign() = default;
operator =CountAssign50   CountAssign& operator=(CountAssign const&) { ++copied; return *this; }
operator =CountAssign51   CountAssign& operator=(CountAssign&&) { ++moved; return *this; }
52 };
53 int CountAssign::copied = 0;
54 int CountAssign::moved = 0;
55 
56 TEST_CONSTEXPR_CXX20
test()57 bool test()
58 {
59     {
60         typedef std::tuple<> T;
61         T t0;
62         T t;
63         t = std::move(t0);
64     }
65     {
66         typedef std::tuple<MoveOnly> T;
67         T t0(MoveOnly(0));
68         T t;
69         t = std::move(t0);
70         assert(std::get<0>(t) == 0);
71     }
72     {
73         typedef std::tuple<MoveOnly, MoveOnly> T;
74         T t0(MoveOnly(0), MoveOnly(1));
75         T t;
76         t = std::move(t0);
77         assert(std::get<0>(t) == 0);
78         assert(std::get<1>(t) == 1);
79     }
80     {
81         typedef std::tuple<MoveOnly, MoveOnly, MoveOnly> T;
82         T t0(MoveOnly(0), MoveOnly(1), MoveOnly(2));
83         T t;
84         t = std::move(t0);
85         assert(std::get<0>(t) == 0);
86         assert(std::get<1>(t) == 1);
87         assert(std::get<2>(t) == 2);
88     }
89     {
90         // test reference assignment.
91         using T = std::tuple<int&, int&&>;
92         int x = 42;
93         int y = 100;
94         int x2 = -1;
95         int y2 = 500;
96         T t(x, std::move(y));
97         T t2(x2, std::move(y2));
98         t = std::move(t2);
99         assert(std::get<0>(t) == x2);
100         assert(&std::get<0>(t) == &x);
101         assert(std::get<1>(t) == y2);
102         assert(&std::get<1>(t) == &y);
103     }
104     return true;
105 }
106 
main(int,char **)107 int main(int, char**)
108 {
109     test();
110 #if TEST_STD_VER >= 20
111     static_assert(test());
112 #endif
113 
114     {
115         // test that the implicitly generated move assignment operator
116         // is properly deleted
117         using T = std::tuple<std::unique_ptr<int>>;
118         static_assert(std::is_move_assignable<T>::value, "");
119         static_assert(!std::is_copy_assignable<T>::value, "");
120     }
121     {
122         using T = std::tuple<int, NonAssignable>;
123         static_assert(!std::is_move_assignable<T>::value, "");
124     }
125     {
126         using T = std::tuple<int, MoveAssignable>;
127         static_assert(std::is_move_assignable<T>::value, "");
128     }
129     {
130         // The move should decay to a copy.
131         CountAssign::reset();
132         using T = std::tuple<CountAssign, CopyAssignable>;
133         static_assert(std::is_move_assignable<T>::value, "");
134         T t1;
135         T t2;
136         t1 = std::move(t2);
137         assert(CountAssign::copied == 1);
138         assert(CountAssign::moved == 0);
139     }
140     {
141         using T = std::tuple<int, NonAssignable>;
142         static_assert(!std::is_move_assignable<T>::value, "");
143     }
144     {
145         using T = std::tuple<int, MoveAssignable>;
146         static_assert(std::is_move_assignable<T>::value, "");
147     }
148     {
149         using T = std::tuple<NothrowMoveAssignable, int>;
150         static_assert(std::is_nothrow_move_assignable<T>::value, "");
151     }
152     {
153         using T = std::tuple<PotentiallyThrowingMoveAssignable, int>;
154         static_assert(!std::is_nothrow_move_assignable<T>::value, "");
155     }
156     {
157         // We assign through the reference and don't move out of the incoming ref,
158         // so this doesn't work (but would if the type were CopyAssignable).
159         using T1 = std::tuple<MoveAssignable&, int>;
160         static_assert(!std::is_move_assignable<T1>::value, "");
161 
162         // ... works if it's CopyAssignable
163         using T2 = std::tuple<CopyAssignable&, int>;
164         static_assert(std::is_move_assignable<T2>::value, "");
165 
166         // For rvalue-references, we can move-assign if the type is MoveAssignable
167         // or CopyAssignable (since in the worst case the move will decay into a copy).
168         using T3 = std::tuple<MoveAssignable&&, int>;
169         using T4 = std::tuple<CopyAssignable&&, int>;
170         static_assert(std::is_move_assignable<T3>::value, "");
171         static_assert(std::is_move_assignable<T4>::value, "");
172 
173         // In all cases, we can't move-assign if the types are not assignable,
174         // since we assign through the reference.
175         using T5 = std::tuple<NonAssignable&, int>;
176         using T6 = std::tuple<NonAssignable&&, int>;
177         static_assert(!std::is_move_assignable<T5>::value, "");
178         static_assert(!std::is_move_assignable<T6>::value, "");
179     }
180 
181     return 0;
182 }
183