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
27template <class Tp>
28using uncvref_t = typename std::remove_cv<typename std::remove_reference<Tp>::type>::type;
29
30template <class Tuple, class = uncvref_t<Tuple>>
31struct IsTuple : std::false_type {};
32
33template <class Tuple, class ...Args>
34struct IsTuple<Tuple, std::tuple<Args...>> : std::true_type {};
35
36struct 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
49struct 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
62struct 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
72struct 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
81int 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

source code of libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp