264 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// <tuple>10 11// template <class... Types> class tuple;12 13// template <class... UTypes>14// tuple& operator=(tuple<UTypes...>&& u);15 16// UNSUPPORTED: c++0317 18#include <tuple>19#include <string>20#include <memory>21#include <utility>22#include <cassert>23 24#include "test_macros.h"25 26struct B {27 int id_;28 explicit B(int i = 0) : id_(i) {}29 B(const B&) = default;30 B& operator=(const B&) = default;31 virtual ~B() {}32};33 34struct D : B {35 explicit D(int i) : B(i) {}36};37 38struct E {39 constexpr E() = default;40 TEST_CONSTEXPR_CXX14 E& operator=(int) {41 return *this;42 }43};44 45struct NothrowMoveAssignable {46 NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; }47};48 49struct PotentiallyThrowingMoveAssignable {50 PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; }51};52 53struct NonAssignable {54 NonAssignable& operator=(NonAssignable const&) = delete;55 NonAssignable& operator=(NonAssignable&&) = delete;56};57 58struct MoveAssignable {59 MoveAssignable& operator=(MoveAssignable const&) = delete;60 MoveAssignable& operator=(MoveAssignable&&) = default;61};62 63struct CopyAssignable {64 CopyAssignable& operator=(CopyAssignable const&) = default;65 CopyAssignable& operator=(CopyAssignable&&) = delete;66};67 68struct TrackMove69{70 TrackMove() : value(0), moved_from(false) { }71 explicit TrackMove(int v) : value(v), moved_from(false) { }72 TrackMove(TrackMove const& other) : value(other.value), moved_from(false) { }73 TrackMove(TrackMove&& other) : value(other.value), moved_from(false) {74 other.moved_from = true;75 }76 TrackMove& operator=(TrackMove const& other) {77 value = other.value;78 moved_from = false;79 return *this;80 }81 TrackMove& operator=(TrackMove&& other) {82 value = other.value;83 moved_from = false;84 other.moved_from = true;85 return *this;86 }87 88 int value;89 bool moved_from;90};91 92TEST_CONSTEXPR_CXX2093bool test()94{95 {96 typedef std::tuple<long> T0;97 typedef std::tuple<long long> T1;98 T0 t0(2);99 T1 t1;100 t1 = std::move(t0);101 assert(std::get<0>(t1) == 2);102 }103 {104 typedef std::tuple<long, char> T0;105 typedef std::tuple<long long, int> T1;106 T0 t0(2, 'a');107 T1 t1;108 t1 = std::move(t0);109 assert(std::get<0>(t1) == 2);110 assert(std::get<1>(t1) == int('a'));111 }112 {113 // Test that tuple evaluates correctly applies an lvalue reference114 // before evaluating is_assignable (i.e. 'is_assignable<int&, int&&>')115 // instead of evaluating 'is_assignable<int&&, int&&>' which is false.116 int x = 42;117 int y = 43;118 std::tuple<int&&, E> t(std::move(x), E{});119 std::tuple<int&&, int> t2(std::move(y), 44);120 t = std::move(t2);121 assert(std::get<0>(t) == 43);122 assert(&std::get<0>(t) == &x);123 }124 125 return true;126}127 128int main(int, char**)129{130 test();131#if TEST_STD_VER >= 20132 static_assert(test());133#endif134 135 {136 typedef std::tuple<long, char, D> T0;137 typedef std::tuple<long long, int, B> T1;138 T0 t0(2, 'a', D(3));139 T1 t1;140 t1 = std::move(t0);141 assert(std::get<0>(t1) == 2);142 assert(std::get<1>(t1) == int('a'));143 assert(std::get<2>(t1).id_ == 3);144 }145 {146 D d(3);147 D d2(2);148 typedef std::tuple<long, char, D&> T0;149 typedef std::tuple<long long, int, B&> T1;150 T0 t0(2, 'a', d2);151 T1 t1(1, 'b', d);152 t1 = std::move(t0);153 assert(std::get<0>(t1) == 2);154 assert(std::get<1>(t1) == int('a'));155 assert(std::get<2>(t1).id_ == 2);156 }157 {158 typedef std::tuple<long, char, std::unique_ptr<D>> T0;159 typedef std::tuple<long long, int, std::unique_ptr<B>> T1;160 T0 t0(2, 'a', std::unique_ptr<D>(new D(3)));161 T1 t1;162 t1 = std::move(t0);163 assert(std::get<0>(t1) == 2);164 assert(std::get<1>(t1) == int('a'));165 assert(std::get<2>(t1)->id_ == 3);166 }167 168 {169 using T = std::tuple<int, NonAssignable>;170 using U = std::tuple<NonAssignable, int>;171 static_assert(!std::is_assignable<T&, U&&>::value, "");172 static_assert(!std::is_assignable<U&, T&&>::value, "");173 }174 {175 typedef std::tuple<NothrowMoveAssignable, long> T0;176 typedef std::tuple<NothrowMoveAssignable, int> T1;177 static_assert(std::is_nothrow_assignable<T0&, T1&&>::value, "");178 }179 {180 typedef std::tuple<PotentiallyThrowingMoveAssignable, long> T0;181 typedef std::tuple<PotentiallyThrowingMoveAssignable, int> T1;182 static_assert(std::is_assignable<T0&, T1&&>::value, "");183 static_assert(!std::is_nothrow_assignable<T0&, T1&&>::value, "");184 }185 {186 // We assign through the reference and don't move out of the incoming ref,187 // so this doesn't work (but would if the type were CopyAssignable).188 {189 using T1 = std::tuple<MoveAssignable&, long>;190 using T2 = std::tuple<MoveAssignable&, int>;191 static_assert(!std::is_assignable<T1&, T2&&>::value, "");192 }193 194 // ... works if it's CopyAssignable195 {196 using T1 = std::tuple<CopyAssignable&, long>;197 using T2 = std::tuple<CopyAssignable&, int>;198 static_assert(std::is_assignable<T1&, T2&&>::value, "");199 }200 201 // For rvalue-references, we can move-assign if the type is MoveAssignable202 // or CopyAssignable (since in the worst case the move will decay into a copy).203 {204 using T1 = std::tuple<MoveAssignable&&, long>;205 using T2 = std::tuple<MoveAssignable&&, int>;206 static_assert(std::is_assignable<T1&, T2&&>::value, "");207 208 using T3 = std::tuple<CopyAssignable&&, long>;209 using T4 = std::tuple<CopyAssignable&&, int>;210 static_assert(std::is_assignable<T3&, T4&&>::value, "");211 }212 213 // In all cases, we can't move-assign if the types are not assignable,214 // since we assign through the reference.215 {216 using T1 = std::tuple<NonAssignable&, long>;217 using T2 = std::tuple<NonAssignable&, int>;218 static_assert(!std::is_assignable<T1&, T2&&>::value, "");219 220 using T3 = std::tuple<NonAssignable&&, long>;221 using T4 = std::tuple<NonAssignable&&, int>;222 static_assert(!std::is_assignable<T3&, T4&&>::value, "");223 }224 }225 {226 // Make sure that we don't incorrectly move out of the source's reference.227 using Dest = std::tuple<TrackMove, long>;228 using Source = std::tuple<TrackMove&, int>;229 TrackMove track{3};230 Source src(track, 4);231 assert(!track.moved_from);232 233 Dest dst;234 dst = std::move(src); // here we should make a copy235 assert(!track.moved_from);236 assert(std::get<0>(dst).value == 3);237 }238 {239 // But we do move out of the source's reference if it's a rvalue ref240 using Dest = std::tuple<TrackMove, long>;241 using Source = std::tuple<TrackMove&&, int>;242 TrackMove track{3};243 Source src(std::move(track), 4);244 assert(!track.moved_from); // we just took a reference245 246 Dest dst;247 dst = std::move(src);248 assert(track.moved_from);249 assert(std::get<0>(dst).value == 3);250 }251 {252 // If the source holds a value, then we move out of it too253 using Dest = std::tuple<TrackMove, long>;254 using Source = std::tuple<TrackMove, int>;255 Source src(TrackMove{3}, 4);256 Dest dst;257 dst = std::move(src);258 assert(std::get<0>(src).moved_from);259 assert(std::get<0>(dst).value == 3);260 }261 262 return 0;263}264