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 | // UNSUPPORTED: c++03 |
10 | |
11 | // <tuple> |
12 | |
13 | // template <class... Types> class tuple; |
14 | |
15 | // Check that the tuple-like ctors are properly disabled when the UTypes... |
16 | // constructor should be selected. |
17 | // |
18 | // See https://llvm.org/PR22806. |
19 | |
20 | #include <cassert> |
21 | #include <memory> |
22 | #include <tuple> |
23 | #include <type_traits> |
24 | |
25 | #include "test_macros.h" |
26 | |
27 | template <class Tp> |
28 | using uncvref_t = typename std::remove_cv<typename std::remove_reference<Tp>::type>::type; |
29 | |
30 | template <class Tuple, class = uncvref_t<Tuple>> |
31 | struct IsTuple : std::false_type {}; |
32 | |
33 | template <class Tuple, class ...Args> |
34 | struct IsTuple<Tuple, std::tuple<Args...>> : std::true_type {}; |
35 | |
36 | struct ConstructibleFromTupleAndInt { |
37 | enum State { FromTuple, FromInt, Copied, Moved }; |
38 | State state; |
39 | |
40 | ConstructibleFromTupleAndInt(ConstructibleFromTupleAndInt const&) : state(Copied) {} |
41 | ConstructibleFromTupleAndInt(ConstructibleFromTupleAndInt &&) : state(Moved) {} |
42 | |
43 | template <class Tuple, class = typename std::enable_if<IsTuple<Tuple>::value>::type> |
44 | explicit ConstructibleFromTupleAndInt(Tuple&&) : state(FromTuple) {} |
45 | |
46 | explicit ConstructibleFromTupleAndInt(int) : state(FromInt) {} |
47 | }; |
48 | |
49 | struct ConvertibleFromTupleAndInt { |
50 | enum State { FromTuple, FromInt, Copied, Moved }; |
51 | State state; |
52 | |
53 | ConvertibleFromTupleAndInt(ConvertibleFromTupleAndInt const&) : state(Copied) {} |
54 | ConvertibleFromTupleAndInt(ConvertibleFromTupleAndInt &&) : state(Moved) {} |
55 | |
56 | template <class Tuple, class = typename std::enable_if<IsTuple<Tuple>::value>::type> |
57 | ConvertibleFromTupleAndInt(Tuple&&) : state(FromTuple) {} |
58 | |
59 | ConvertibleFromTupleAndInt(int) : state(FromInt) {} |
60 | }; |
61 | |
62 | struct ConstructibleFromInt { |
63 | enum State { FromInt, Copied, Moved }; |
64 | State state; |
65 | |
66 | ConstructibleFromInt(ConstructibleFromInt const&) : state(Copied) {} |
67 | ConstructibleFromInt(ConstructibleFromInt &&) : state(Moved) {} |
68 | |
69 | explicit ConstructibleFromInt(int) : state(FromInt) {} |
70 | }; |
71 | |
72 | struct ConvertibleFromInt { |
73 | enum State { FromInt, Copied, Moved }; |
74 | State state; |
75 | |
76 | ConvertibleFromInt(ConvertibleFromInt const&) : state(Copied) {} |
77 | ConvertibleFromInt(ConvertibleFromInt &&) : state(Moved) {} |
78 | ConvertibleFromInt(int) : state(FromInt) {} |
79 | }; |
80 | |
81 | int main(int, char**) |
82 | { |
83 | // Test for the creation of dangling references when a tuple is used to |
84 | // store a reference to another tuple as its only element. |
85 | // Ex std::tuple<std::tuple<int>&&>. |
86 | // In this case the constructors 1) 'tuple(UTypes&&...)' |
87 | // and 2) 'tuple(TupleLike&&)' need to be manually disambiguated because |
88 | // when both #1 and #2 participate in partial ordering #2 will always |
89 | // be chosen over #1. |
90 | // See PR22806 and LWG issue #2549 for more information. |
91 | // (https://llvm.org/PR22806) |
92 | using T = std::tuple<int>; |
93 | std::allocator<int> A; |
94 | { // rvalue reference |
95 | T t1(42); |
96 | std::tuple< T&& > t2(std::move(t1)); |
97 | assert(&std::get<0>(t2) == &t1); |
98 | } |
99 | { // const lvalue reference |
100 | T t1(42); |
101 | |
102 | std::tuple< T const & > t2(t1); |
103 | assert(&std::get<0>(t2) == &t1); |
104 | |
105 | std::tuple< T const & > t3(static_cast<T const&>(t1)); |
106 | assert(&std::get<0>(t3) == &t1); |
107 | } |
108 | { // lvalue reference |
109 | T t1(42); |
110 | |
111 | std::tuple< T & > t2(t1); |
112 | assert(&std::get<0>(t2) == &t1); |
113 | } |
114 | { // const rvalue reference |
115 | T t1(42); |
116 | |
117 | std::tuple< T const && > t2(std::move(t1)); |
118 | assert(&std::get<0>(t2) == &t1); |
119 | } |
120 | { // rvalue reference via uses-allocator |
121 | T t1(42); |
122 | std::tuple< T&& > t2(std::allocator_arg, A, std::move(t1)); |
123 | assert(&std::get<0>(t2) == &t1); |
124 | } |
125 | { // const lvalue reference via uses-allocator |
126 | T t1(42); |
127 | |
128 | std::tuple< T const & > t2(std::allocator_arg, A, t1); |
129 | assert(&std::get<0>(t2) == &t1); |
130 | |
131 | std::tuple< T const & > t3(std::allocator_arg, A, static_cast<T const&>(t1)); |
132 | assert(&std::get<0>(t3) == &t1); |
133 | } |
134 | { // lvalue reference via uses-allocator |
135 | T t1(42); |
136 | |
137 | std::tuple< T & > t2(std::allocator_arg, A, t1); |
138 | assert(&std::get<0>(t2) == &t1); |
139 | } |
140 | { // const rvalue reference via uses-allocator |
141 | T const t1(42); |
142 | std::tuple< T const && > t2(std::allocator_arg, A, std::move(t1)); |
143 | assert(&std::get<0>(t2) == &t1); |
144 | } |
145 | // Test constructing a 1-tuple of the form tuple<UDT> from another 1-tuple |
146 | // 'tuple<T>' where UDT *can* be constructed from 'tuple<T>'. In this case |
147 | // the 'tuple(UTypes...)' ctor should be chosen and 'UDT' constructed from |
148 | // 'tuple<T>'. |
149 | { |
150 | using VT = ConstructibleFromTupleAndInt; |
151 | std::tuple<int> t1(42); |
152 | std::tuple<VT> t2(t1); |
153 | assert(std::get<0>(t2).state == VT::FromTuple); |
154 | } |
155 | { |
156 | using VT = ConvertibleFromTupleAndInt; |
157 | std::tuple<int> t1(42); |
158 | std::tuple<VT> t2 = {t1}; |
159 | assert(std::get<0>(t2).state == VT::FromTuple); |
160 | } |
161 | // Test constructing a 1-tuple of the form tuple<UDT> from another 1-tuple |
162 | // 'tuple<T>' where UDT cannot be constructed from 'tuple<T>' but can |
163 | // be constructed from 'T'. In this case the tuple-like ctor should be |
164 | // chosen and 'UDT' constructed from 'T' |
165 | { |
166 | using VT = ConstructibleFromInt; |
167 | std::tuple<int> t1(42); |
168 | std::tuple<VT> t2(t1); |
169 | assert(std::get<0>(t2).state == VT::FromInt); |
170 | } |
171 | { |
172 | using VT = ConvertibleFromInt; |
173 | std::tuple<int> t1(42); |
174 | std::tuple<VT> t2 = {t1}; |
175 | assert(std::get<0>(t2).state == VT::FromInt); |
176 | } |
177 | |
178 | return 0; |
179 | } |
180 | |