xref: /llvm-project/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp (revision 618862e89a022b2e8f73a62bed7c91654060dbab)
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 // template <class U1, class U2>
14 //   tuple& operator=(pair<U1, U2>&& u);
15 
16 // UNSUPPORTED: c++03
17 
18 #include <tuple>
19 #include <utility>
20 #include <memory>
21 #include <cassert>
22 
23 #include "test_macros.h"
24 
25 struct B
26 {
27     int id_;
28 
BB29     explicit B(int i = 0) : id_(i) {}
30 
~BB31     virtual ~B() {}
32 };
33 
34 struct D
35     : B
36 {
DD37     explicit D(int i) : B(i) {}
38 };
39 
40 struct TrackMove
41 {
TrackMoveTrackMove42     TrackMove() : value(0), moved_from(false) { }
TrackMoveTrackMove43     explicit TrackMove(int v) : value(v), moved_from(false) { }
TrackMoveTrackMove44     TrackMove(TrackMove const& other) : value(other.value), moved_from(false) { }
TrackMoveTrackMove45     TrackMove(TrackMove&& other) : value(other.value), moved_from(false) {
46         other.moved_from = true;
47     }
operator =TrackMove48     TrackMove& operator=(TrackMove const& other) {
49         value = other.value;
50         moved_from = false;
51         return *this;
52     }
operator =TrackMove53     TrackMove& operator=(TrackMove&& other) {
54         value = other.value;
55         moved_from = false;
56         other.moved_from = true;
57         return *this;
58     }
59 
60     int value;
61     bool moved_from;
62 };
63 
64 struct NonAssignable
65 {
66   NonAssignable& operator=(NonAssignable const&) = delete;
67   NonAssignable& operator=(NonAssignable&&) = delete;
68 };
69 
70 struct MoveAssignable
71 {
72   MoveAssignable& operator=(MoveAssignable const&) = delete;
73   MoveAssignable& operator=(MoveAssignable&&) = default;
74 };
75 
76 struct CopyAssignable
77 {
78   CopyAssignable& operator=(CopyAssignable const&) = default;
79   CopyAssignable& operator=(CopyAssignable&&) = delete;
80 };
81 
82 struct NothrowMoveAssignable
83 {
operator =NothrowMoveAssignable84     NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }
85 };
86 
87 struct PotentiallyThrowingMoveAssignable
88 {
operator =PotentiallyThrowingMoveAssignable89     PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }
90 };
91 
main(int,char **)92 int main(int, char**)
93 {
94     {
95         typedef std::pair<long, std::unique_ptr<D>> T0;
96         typedef std::tuple<long long, std::unique_ptr<B>> T1;
97         T0 t0(2, std::unique_ptr<D>(new D(3)));
98         T1 t1;
99         t1 = std::move(t0);
100         assert(std::get<0>(t1) == 2);
101         assert(std::get<1>(t1)->id_ == 3);
102     }
103     {
104         using T = std::tuple<int, NonAssignable>;
105         using P = std::pair<int, NonAssignable>;
106         static_assert(!std::is_assignable<T&, P&&>::value, "");
107     }
108     {
109       using T = std::tuple<int, int, int>;
110       using P = std::pair<int, int>;
111       static_assert(!std::is_assignable<T&, P&&>::value, "");
112     }
113     {
114         typedef std::tuple<NothrowMoveAssignable, long> Tuple;
115         typedef std::pair<NothrowMoveAssignable, int> Pair;
116         static_assert(std::is_nothrow_assignable<Tuple&, Pair&&>::value, "");
117         static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "");
118     }
119     {
120         typedef std::tuple<PotentiallyThrowingMoveAssignable, long> Tuple;
121         typedef std::pair<PotentiallyThrowingMoveAssignable, int> Pair;
122         static_assert(std::is_assignable<Tuple&, Pair&&>::value, "");
123         static_assert(!std::is_nothrow_assignable<Tuple&, Pair&&>::value, "");
124         static_assert(!std::is_assignable<Tuple&, Pair const&&>::value, "");
125     }
126     {
127         // We assign through the reference and don't move out of the incoming ref,
128         // so this doesn't work (but would if the type were CopyAssignable).
129         {
130             using T = std::tuple<MoveAssignable&, int>;
131             using P = std::pair<MoveAssignable&, int>;
132             static_assert(!std::is_assignable<T&, P&&>::value, "");
133         }
134 
135         // ... works if it's CopyAssignable
136         {
137             using T = std::tuple<CopyAssignable&, int>;
138             using P = std::pair<CopyAssignable&, int>;
139             static_assert(std::is_assignable<T&, P&&>::value, "");
140         }
141 
142         // For rvalue-references, we can move-assign if the type is MoveAssignable
143         // or CopyAssignable (since in the worst case the move will decay into a copy).
144         {
145             using T1 = std::tuple<MoveAssignable&&, int>;
146             using P1 = std::pair<MoveAssignable&&, int>;
147             static_assert(std::is_assignable<T1&, P1&&>::value, "");
148 
149             using T2 = std::tuple<CopyAssignable&&, int>;
150             using P2 = std::pair<CopyAssignable&&, int>;
151             static_assert(std::is_assignable<T2&, P2&&>::value, "");
152         }
153 
154         // In all cases, we can't move-assign if the types are not assignable,
155         // since we assign through the reference.
156         {
157             using T1 = std::tuple<NonAssignable&, int>;
158             using P1 = std::pair<NonAssignable&, int>;
159             static_assert(!std::is_assignable<T1&, P1&&>::value, "");
160 
161             using T2 = std::tuple<NonAssignable&&, int>;
162             using P2 = std::pair<NonAssignable&&, int>;
163             static_assert(!std::is_assignable<T2&, P2&&>::value, "");
164         }
165     }
166     {
167         // Make sure that we don't incorrectly move out of the source's reference.
168         using Dest = std::tuple<TrackMove, int>;
169         using Source = std::pair<TrackMove&, int>;
170         TrackMove track{3};
171         Source src(track, 4);
172         assert(!track.moved_from);
173 
174         Dest dst;
175         dst = std::move(src); // here we should make a copy
176         assert(!track.moved_from);
177         assert(std::get<0>(dst).value == 3);
178     }
179     {
180         // But we do move out of the source's reference if it's a rvalue ref
181         using Dest = std::tuple<TrackMove, int>;
182         using Source = std::pair<TrackMove&&, int>;
183         TrackMove track{3};
184         Source src(std::move(track), 4);
185         assert(!track.moved_from); // we just took a reference
186 
187         Dest dst;
188         dst = std::move(src);
189         assert(track.moved_from);
190         assert(std::get<0>(dst).value == 3);
191     }
192     {
193         // If the pair holds a value, then we move out of it too
194         using Dest = std::tuple<TrackMove, int>;
195         using Source = std::pair<TrackMove, int>;
196         Source src(TrackMove{3}, 4);
197         Dest dst;
198         dst = std::move(src);
199         assert(src.first.moved_from);
200         assert(std::get<0>(dst).value == 3);
201     }
202 
203     return 0;
204 }
205