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, c++11, c++14, c++17, c++20 |
10 | |
11 | // <utility> |
12 | |
13 | // template <class T1, class T2> struct pair |
14 | |
15 | // template <pair-like P> |
16 | // constexpr explicit(see-below) pair(P&&); // since C++23 |
17 | |
18 | #include <array> |
19 | #include <cassert> |
20 | #include <ranges> |
21 | #include <string> |
22 | #include <tuple> |
23 | #include <type_traits> |
24 | #include <utility> |
25 | |
26 | namespace my_ns{ |
27 | |
28 | struct MyPairLike { |
29 | |
30 | template <std::size_t N> |
31 | friend int get(MyPairLike const&) |
32 | { |
33 | return 0; |
34 | } |
35 | |
36 | }; |
37 | |
38 | } // namespace my_ns |
39 | |
40 | namespace std { |
41 | |
42 | template <> |
43 | struct tuple_size<my_ns::MyPairLike> : std::integral_constant<std::size_t, 2> {}; |
44 | |
45 | template <std::size_t N> |
46 | struct tuple_element<N, my_ns::MyPairLike> { |
47 | using type = int; |
48 | }; |
49 | |
50 | } // namespace std |
51 | |
52 | // https://github.com/llvm/llvm-project/issues/65620 |
53 | // This used to be a hard error |
54 | static_assert(!std::is_constructible_v<std::pair<int,int>, my_ns::MyPairLike const&>); |
55 | |
56 | |
57 | constexpr bool test() { |
58 | // Make sure construction works from array, tuple, and ranges::subrange |
59 | { |
60 | // Check from std::array |
61 | { |
62 | std::array<int, 2> a = {1, 2}; |
63 | std::pair<int, int> p(a); |
64 | assert(p.first == 1); |
65 | assert(p.second == 2); |
66 | static_assert(!std::is_constructible_v<std::pair<int, int>, std::array<int, 1>>); // too small |
67 | static_assert( std::is_constructible_v<std::pair<int, int>, std::array<int, 2>>); // works (test the test) |
68 | static_assert(!std::is_constructible_v<std::pair<int, int>, std::array<int, 3>>); // too large |
69 | } |
70 | |
71 | // Check from std::tuple |
72 | { |
73 | std::tuple<int, int> a = {1, 2}; |
74 | std::pair<int, int> p(a); |
75 | assert(p.first == 1); |
76 | assert(p.second == 2); |
77 | static_assert(!std::is_constructible_v<std::pair<int, int>, std::tuple<int>>); // too small |
78 | static_assert( std::is_constructible_v<std::pair<int, int>, std::tuple<int, int>>); // works (test the test) |
79 | static_assert(!std::is_constructible_v<std::pair<int, int>, std::tuple<int, int, int>>); // too large |
80 | } |
81 | |
82 | // Check that the constructor excludes ranges::subrange |
83 | { |
84 | int data[] = {1, 2, 3, 4, 5}; |
85 | const std::ranges::subrange a(data); |
86 | // Note the expression below would be ambiguous if pair's |
87 | // constructor does not exclude subrange |
88 | std::pair<int*, int*> p = a; |
89 | assert(p.first == data + 0); |
90 | assert(p.second == data + 5); |
91 | } |
92 | } |
93 | |
94 | // Make sure we allow element conversion from a pair-like |
95 | { |
96 | std::tuple<int, char const*> a = {34, "hello world" }; |
97 | std::pair<long, std::string> p(a); |
98 | assert(p.first == 34); |
99 | assert(p.second == std::string("hello world" )); |
100 | static_assert(!std::is_constructible_v<std::pair<long, std::string>, std::tuple<char*, std::string>>); // first not convertible |
101 | static_assert(!std::is_constructible_v<std::pair<long, std::string>, std::tuple<long, void*>>); // second not convertible |
102 | static_assert( std::is_constructible_v<std::pair<long, std::string>, std::tuple<long, std::string>>); // works (test the test) |
103 | } |
104 | |
105 | // Make sure we forward the pair-like elements |
106 | { |
107 | struct NoCopy { |
108 | NoCopy() = default; |
109 | NoCopy(NoCopy const&) = delete; |
110 | NoCopy(NoCopy&&) = default; |
111 | }; |
112 | std::tuple<NoCopy, NoCopy> a; |
113 | std::pair<NoCopy, NoCopy> p(std::move(a)); |
114 | (void)p; |
115 | } |
116 | |
117 | // Make sure the constructor is implicit iff both elements can be converted |
118 | { |
119 | struct To { }; |
120 | struct FromImplicit { |
121 | constexpr operator To() const { return To{}; } |
122 | }; |
123 | struct FromExplicit { |
124 | constexpr explicit operator To() const { return To{}; } |
125 | }; |
126 | // If both are convertible, the constructor is not explicit |
127 | { |
128 | std::tuple<FromImplicit, float> a = {FromImplicit{}, 2.3f}; |
129 | std::pair<To, double> p = a; |
130 | (void)p; |
131 | static_assert(std::is_convertible_v<std::tuple<FromImplicit, float>, std::pair<To, double>>); |
132 | } |
133 | // Otherwise, the constructor is explicit |
134 | { |
135 | static_assert( std::is_constructible_v<std::pair<To, int>, std::tuple<FromExplicit, int>>); |
136 | static_assert(!std::is_convertible_v<std::tuple<FromExplicit, int>, std::pair<To, int>>); |
137 | |
138 | static_assert( std::is_constructible_v<std::pair<int, To>, std::tuple<int, FromExplicit>>); |
139 | static_assert(!std::is_convertible_v<std::tuple<int, FromExplicit>, std::pair<int, To>>); |
140 | |
141 | static_assert( std::is_constructible_v<std::pair<To, To>, std::tuple<FromExplicit, FromExplicit>>); |
142 | static_assert(!std::is_convertible_v<std::tuple<FromExplicit, FromExplicit>, std::pair<To, To>>); |
143 | } |
144 | } |
145 | return true; |
146 | } |
147 | |
148 | int main(int, char**) { |
149 | test(); |
150 | static_assert(test()); |
151 | |
152 | return 0; |
153 | } |
154 | |