Skip to content

Commit 06de4d5

Browse files
[libc++] Properly decay functions in CTAD for pair (#134544)
This patch makes instantiation of `pair` in CTAD a bit lazier to avoid instantiating invalid `pair` specialization before the decaying explicit deduction guide works.
1 parent 7962820 commit 06de4d5

File tree

2 files changed

+119
-45
lines changed

2 files changed

+119
-45
lines changed

libcxx/include/__utility/pair.h

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ _LIBCPP_PUSH_MACROS
4949

5050
_LIBCPP_BEGIN_NAMESPACE_STD
5151

52+
#ifndef _LIBCPP_CXX03_LANG
53+
54+
template <class _T1, class _T2>
55+
struct __check_pair_construction {
56+
template <int&...>
57+
static _LIBCPP_HIDE_FROM_ABI constexpr bool __enable_implicit_default() {
58+
return __is_implicitly_default_constructible<_T1>::value && __is_implicitly_default_constructible<_T2>::value;
59+
}
60+
61+
template <int&...>
62+
static _LIBCPP_HIDE_FROM_ABI constexpr bool __enable_default() {
63+
return is_default_constructible<_T1>::value && is_default_constructible<_T2>::value;
64+
}
65+
66+
template <class _U1, class _U2>
67+
static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_pair_constructible() {
68+
return is_constructible<_T1, _U1>::value && is_constructible<_T2, _U2>::value;
69+
}
70+
71+
template <class _U1, class _U2>
72+
static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_implicit() {
73+
return is_convertible<_U1, _T1>::value && is_convertible<_U2, _T2>::value;
74+
}
75+
};
76+
77+
#endif
78+
5279
template <class, class>
5380
struct __non_trivially_copyable_base {
5481
_LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI __non_trivially_copyable_base() _NOEXCEPT {}
@@ -104,40 +131,16 @@ struct pair
104131
return *this;
105132
}
106133
#else
107-
struct _CheckArgs {
108-
template <int&...>
109-
static _LIBCPP_HIDE_FROM_ABI constexpr bool __enable_implicit_default() {
110-
return __is_implicitly_default_constructible<_T1>::value && __is_implicitly_default_constructible<_T2>::value;
111-
}
112-
113-
template <int&...>
114-
static _LIBCPP_HIDE_FROM_ABI constexpr bool __enable_default() {
115-
return is_default_constructible<_T1>::value && is_default_constructible<_T2>::value;
116-
}
117-
118-
template <class _U1, class _U2>
119-
static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_pair_constructible() {
120-
return is_constructible<first_type, _U1>::value && is_constructible<second_type, _U2>::value;
121-
}
122-
123-
template <class _U1, class _U2>
124-
static _LIBCPP_HIDE_FROM_ABI constexpr bool __is_implicit() {
125-
return is_convertible<_U1, first_type>::value && is_convertible<_U2, second_type>::value;
126-
}
127-
};
128-
129-
template <bool _MaybeEnable>
130-
using _CheckArgsDep _LIBCPP_NODEBUG = __conditional_t<_MaybeEnable, _CheckArgs, void>;
131-
132-
template <bool _Dummy = true, __enable_if_t<_CheckArgsDep<_Dummy>::__enable_default(), int> = 0>
133-
explicit(!_CheckArgsDep<_Dummy>::__enable_implicit_default()) _LIBCPP_HIDE_FROM_ABI constexpr pair() noexcept(
134+
template <class _CheckArgsDep = __check_pair_construction<_T1, _T2>,
135+
__enable_if_t<_CheckArgsDep::__enable_default(), int> = 0>
136+
explicit(!_CheckArgsDep::__enable_implicit_default()) _LIBCPP_HIDE_FROM_ABI constexpr pair() noexcept(
134137
is_nothrow_default_constructible<first_type>::value && is_nothrow_default_constructible<second_type>::value)
135138
: first(), second() {}
136139

137-
template <bool _Dummy = true,
138-
__enable_if_t<_CheckArgsDep<_Dummy>::template __is_pair_constructible<_T1 const&, _T2 const&>(), int> = 0>
140+
template <class _CheckArgsDep = __check_pair_construction<_T1, _T2>,
141+
__enable_if_t<_CheckArgsDep::template __is_pair_constructible<_T1 const&, _T2 const&>(), int> = 0>
139142
_LIBCPP_HIDE_FROM_ABI
140-
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgsDep<_Dummy>::template __is_implicit<_T1 const&, _T2 const&>())
143+
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgsDep::template __is_implicit<_T1 const&, _T2 const&>())
141144
pair(_T1 const& __t1, _T2 const& __t2) noexcept(is_nothrow_copy_constructible<first_type>::value &&
142145
is_nothrow_copy_constructible<second_type>::value)
143146
: first(__t1), second(__t2) {}
@@ -150,41 +153,52 @@ struct pair
150153
class _U1,
151154
class _U2,
152155
# endif
153-
__enable_if_t<_CheckArgs::template __is_pair_constructible<_U1, _U2>(), int> = 0 >
154-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>())
156+
__enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1, _U2>(), int> = 0 >
157+
_LIBCPP_HIDE_FROM_ABI
158+
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1, _U2>())
155159
pair(_U1&& __u1, _U2&& __u2) noexcept(is_nothrow_constructible<first_type, _U1>::value &&
156160
is_nothrow_constructible<second_type, _U2>::value)
157161
: first(std::forward<_U1>(__u1)), second(std::forward<_U2>(__u2)) {
158162
}
159163

160164
# if _LIBCPP_STD_VER >= 23
161-
template <class _U1, class _U2, __enable_if_t<_CheckArgs::template __is_pair_constructible<_U1&, _U2&>(), int> = 0>
162-
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_CheckArgs::template __is_implicit<_U1&, _U2&>())
165+
template <class _U1,
166+
class _U2,
167+
__enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1&, _U2&>(), int> = 0>
168+
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1&, _U2&>())
163169
pair(pair<_U1, _U2>& __p) noexcept((is_nothrow_constructible<first_type, _U1&>::value &&
164170
is_nothrow_constructible<second_type, _U2&>::value))
165171
: first(__p.first), second(__p.second) {}
166172
# endif
167173

168-
template <class _U1,
169-
class _U2,
170-
__enable_if_t<_CheckArgs::template __is_pair_constructible<_U1 const&, _U2 const&>(), int> = 0>
171-
_LIBCPP_HIDE_FROM_ABI
172-
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1 const&, _U2 const&>())
174+
template <
175+
class _U1,
176+
class _U2,
177+
__enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1 const&, _U2 const&>(),
178+
int> = 0>
179+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(
180+
!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1 const&, _U2 const&>())
173181
pair(pair<_U1, _U2> const& __p) noexcept(is_nothrow_constructible<first_type, _U1 const&>::value &&
174182
is_nothrow_constructible<second_type, _U2 const&>::value)
175183
: first(__p.first), second(__p.second) {}
176184

177-
template <class _U1, class _U2, __enable_if_t<_CheckArgs::template __is_pair_constructible<_U1, _U2>(), int> = 0>
178-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_CheckArgs::template __is_implicit<_U1, _U2>())
185+
template <class _U1,
186+
class _U2,
187+
__enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<_U1, _U2>(), int> = 0>
188+
_LIBCPP_HIDE_FROM_ABI
189+
_LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!__check_pair_construction<_T1, _T2>::template __is_implicit<_U1, _U2>())
179190
pair(pair<_U1, _U2>&& __p) noexcept(is_nothrow_constructible<first_type, _U1&&>::value &&
180191
is_nothrow_constructible<second_type, _U2&&>::value)
181192
: first(std::forward<_U1>(__p.first)), second(std::forward<_U2>(__p.second)) {}
182193

183194
# if _LIBCPP_STD_VER >= 23
184-
template <class _U1,
185-
class _U2,
186-
__enable_if_t<_CheckArgs::template __is_pair_constructible<const _U1&&, const _U2&&>(), int> = 0>
187-
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!_CheckArgs::template __is_implicit<const _U1&&, const _U2&&>())
195+
template <
196+
class _U1,
197+
class _U2,
198+
__enable_if_t<__check_pair_construction<_T1, _T2>::template __is_pair_constructible<const _U1&&, const _U2&&>(),
199+
int> = 0>
200+
_LIBCPP_HIDE_FROM_ABI constexpr explicit(
201+
!__check_pair_construction<_T1, _T2>::template __is_implicit<const _U1&&, const _U2&&>())
188202
pair(const pair<_U1, _U2>&& __p) noexcept(is_nothrow_constructible<first_type, const _U1&&>::value &&
189203
is_nothrow_constructible<second_type, const _U2&&>::value)
190204
: first(std::move(__p.first)), second(std::move(__p.second)) {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
// REQUIRES: std-at-least-c++17
10+
11+
// <utility>
12+
13+
// template<class T1, class T2>
14+
// pair(T1, T2) -> pair<T1, T2>;
15+
16+
// Test that the explicit deduction guide for std::pair correctly decays function lvalues and
17+
// behaves different from std::make_pair.
18+
19+
#include <cassert>
20+
#include <functional>
21+
#include <type_traits>
22+
#include <utility>
23+
24+
#include "test_macros.h"
25+
26+
void dummy() {}
27+
28+
constexpr void test_decay() {
29+
char arr[1]{};
30+
std::pair pr(arr, dummy);
31+
32+
ASSERT_SAME_TYPE(decltype(pr), std::pair<char*, void (*)()>);
33+
34+
assert(pr == std::make_pair(arr, dummy));
35+
}
36+
37+
TEST_CONSTEXPR_CXX20 void test_unwrap() {
38+
int n = 0;
39+
std::pair pr(std::ref(n), dummy);
40+
41+
ASSERT_SAME_TYPE(decltype(pr), std::pair<std::reference_wrapper<int>, void (*)()>);
42+
static_assert(!std::is_same_v<decltype(pr), decltype(std::make_pair(std::ref(n), dummy))>);
43+
44+
assert(&(pr.first.get()) == &n);
45+
assert(pr.second == dummy);
46+
}
47+
48+
constexpr bool test() {
49+
test_decay();
50+
if (TEST_STD_AT_LEAST_20_OR_RUNTIME_EVALUATED)
51+
test_unwrap();
52+
return true;
53+
}
54+
55+
int main(int, char**) {
56+
test();
57+
static_assert(test());
58+
59+
return 0;
60+
}

0 commit comments

Comments
 (0)