commit 9e184c70f6a8e8ed8e34d8ffcb9c98e079dd3c31
Author: Jonathan Wakely <jwak...@redhat.com>
Date: Wed Apr 7 18:12:08 2021
libstdc++: Simplify std::pair constraints using concepts
This re-implements the constraints on the std::pair constructors and
assignment operators in C++20 mode, to use concepts.
The non-standard constructors deprecated for PR 99957 are no longer
supported in C++20 mode, which requires some minor testsuite changes.
Otherwise all tests pass in C++20 mode.
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (pair):
* testsuite/20_util/pair/cons/99957.cc: Disable for C++20 and
later.
* testsuite/20_util/pair/cons/explicit_construct.cc: Adjust
expected error messages to also match C++20 errors.
diff --git a/libstdc++-v3/include/bits/stl_pair.h
b/libstdc++-v3/include/bits/stl_pair.h
index 883d7441b3d..bd3f911abb9 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -92,6 +92,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<size_t...>
struct _Index_tuple;
+#if ! __cpp_lib_concepts
// Concept utility functions, reused in conditionally-explicit
// constructors.
// See PR 70437, don't look at is_constructible or
@@ -171,11 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return false;
}
};
+#endif // lib concepts
#endif // C++11
template<typename _U1, typename _U2> class __pair_base
{
-#if __cplusplus >= 201103L
+#if __cplusplus >= 201103L && ! __cpp_lib_concepts
template<typename _T1, typename _T2> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
@@ -196,7 +198,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
template<typename _T1, typename _T2>
struct pair
- : private __pair_base<_T1, _T2>
+ : public __pair_base<_T1, _T2>
{
typedef _T1 first_type; ///< The type of the `first` member
typedef _T2 second_type; ///< The type of the `second` member
@@ -205,7 +207,186 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_T2 second; ///< The second member
#if __cplusplus >= 201103L
- // C++11 (and later) implementation.
+ constexpr pair(const pair&) = default; ///< Copy constructor
+ constexpr pair(pair&&) = default; ///< Move constructor
+
+ template<typename... _Args1, typename... _Args2>
+ _GLIBCXX20_CONSTEXPR
+ pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
+
+ /// Swap the first members and then the second members.
+ _GLIBCXX20_CONSTEXPR void
+ swap(pair& __p)
+ noexcept(__and_<__is_nothrow_swappable<_T1>,
+ __is_nothrow_swappable<_T2>>::value)
+ {
+ using std::swap;
+ swap(first, __p.first);
+ swap(second, __p.second);
+ }
+
+ private:
+ template<typename... _Args1, size_t... _Indexes1,
+ typename... _Args2, size_t... _Indexes2>
+ _GLIBCXX20_CONSTEXPR
+ pair(tuple<_Args1...>&, tuple<_Args2...>&,
+ _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+ public:
+
+#if __cpp_lib_concepts
+ // C++20 implementation using concepts, explicit(bool), fully constexpr.
+
+ /// Default constructor
+ constexpr
+ explicit(__not_<__and_<__is_implicitly_default_constructible<_T1>,
+ __is_implicitly_default_constructible<_T2>>>())
+ pair()
+ requires is_default_constructible_v<_T1>
+ && is_default_constructible_v<_T2>
+ : first(), second()
+ { }
+
+ private:
+
+ /// @cond undocumented
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_constructible()
+ {
+ if constexpr (is_constructible_v<_T1, _U1>)
+ return is_constructible_v<_T2, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_nothrow_constructible()
+ {
+ if constexpr (is_nothrow_constructible_v<_T1, _U1>)
+ return is_nothrow_constructible_v<_T2, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_convertible()
+ {
+ if constexpr (is_convertible_v<_U1, _T1>)
+ return is_convertible_v<_U2, _T2>;
+ return false;
+ }
+ /// @endcond
+
+ public:
+
+ /// Constructor accepting lvalues of `first_type` and `second_type`
+ constexpr explicit(!_S_convertible<const _T1&, const _T2&>())
+ pair(const _T1& __x, const _T2& __y)
+ noexcept(_S_nothrow_constructible<const _T1&, const _T2&>())
+ requires (_S_constructible<const _T1&, const _T2&>())
+ : first(__x), second(__y)
+ { }
+
+ /// Constructor accepting two values of arbitrary types
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<_U1, _U2>())
+ constexpr explicit(!_S_convertible<_U1, _U2>())
+ pair(_U1&& __x, _U2&& __y)
+ noexcept(_S_nothrow_constructible<_U1, _U2>())
+ : first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
+ { }
+
+ /// Converting constructor from a `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<const _U1&, const _U2&>())
+ constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
+ pair(const pair<_U1, _U2>& __p)
+ noexcept(_S_nothrow_constructible<const _U1&, const _U2&>())
+ : first(__p.first), second(__p.second)
+ { }
+
+ /// Converting constructor from a `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ requires (_S_constructible<_U1, _U2>())
+ constexpr explicit(!_S_convertible<_U1, _U2>())
+ pair(pair<_U1, _U2>&& __p)
+ noexcept(_S_nothrow_constructible<_U1, _U2>())
+ : first(std::forward<_U1>(__p.first)),
+ second(std::forward<_U2>(__p.second))
+ { }
+
+ private:
+ /// @cond undocumented
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_assignable()
+ {
+ if constexpr (is_assignable_v<_T1&, _U1>)
+ return is_assignable_v<_T2&, _U2>;
+ return false;
+ }
+
+ template<typename _U1, typename _U2>
+ static constexpr bool
+ _S_nothrow_assignable()
+ {
+ if constexpr (is_nothrow_assignable_v<_T1&, _U1>)
+ return is_nothrow_assignable_v<_T2&, _U2>;
+ return false;
+ }
+ /// @endcond
+
+ public:
+
+ pair& operator=(const pair&) = delete;
+
+ /// Copy assignment operator
+ constexpr pair&
+ operator=(const pair& __p)
+ noexcept(_S_nothrow_assignable<const _T1&, const _T2&>())
+ requires (_S_assignable<const _T1&, const _T2&>())
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Move assignment operator
+ constexpr pair&
+ operator=(pair&& __p)
+ noexcept(_S_nothrow_assignable<_T1, _T2>())
+ requires (_S_assignable<_T1, _T2>())
+ {
+ first = std::forward<first_type>(__p.first);
+ second = std::forward<second_type>(__p.second);
+ return *this;
+ }
+
+ /// Converting assignment from a `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ constexpr pair&
+ operator=(const pair<_U1, _U2>& __p)
+ noexcept(_S_nothrow_assignable<const _U1&, const _U2&>())
+ requires (_S_assignable<const _U1&, const _U2&>())
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Converting assignment from a `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ constexpr pair&
+ operator=(pair<_U1, _U2>&& __p)
+ noexcept(_S_nothrow_assignable<_U1, _U2>())
+ requires (_S_assignable<_U1, _U2>())
+ {
+ first = std::forward<_U1>(__p.first);
+ second = std::forward<_U2>(__p.second);
+ return *this;
+ }
+#else
+ // C++11/14/17 implementation using enable_if, partially constexpr.
/** The default constructor creates @c first and @c second using their
* respective default constructors. */
@@ -281,9 +462,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
explicit constexpr pair(const pair<_U1, _U2>& __p)
: first(__p.first), second(__p.second) { }
- constexpr pair(const pair&) = default; ///< Copy constructor
- constexpr pair(pair&&) = default; ///< Move constructor
-
#if _GLIBCXX_USE_DEPRECATED
private:
/// @cond undocumented
@@ -341,7 +519,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_DEPRECATED_SUGGEST("nullptr")
explicit pair(__null_ptr_constant, _U2&& __y)
: first(nullptr), second(std::forward<_U2>(__y)) { }
-#endif // _GLIBCXX_USE_DEPRECATED
+#endif
template<typename _U1, typename _U2, typename
enable_if<_PCCP::template
@@ -382,10 +560,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(std::forward<_U1>(__p.first)),
second(std::forward<_U2>(__p.second)) { }
- template<typename... _Args1, typename... _Args2>
- _GLIBCXX20_CONSTEXPR
- pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
-
_GLIBCXX20_CONSTEXPR pair&
operator=(typename conditional<
__and_<is_copy_assignable<_T1>,
@@ -433,24 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second = std::forward<_U2>(__p.second);
return *this;
}
-
- /// Swap the first members and then the second members.
- _GLIBCXX20_CONSTEXPR void
- swap(pair& __p)
- noexcept(__and_<__is_nothrow_swappable<_T1>,
- __is_nothrow_swappable<_T2>>::value)
- {
- using std::swap;
- swap(first, __p.first);
- swap(second, __p.second);
- }
-
- private:
- template<typename... _Args1, size_t... _Indexes1,
- typename... _Args2, size_t... _Indexes2>
- _GLIBCXX20_CONSTEXPR
- pair(tuple<_Args1...>&, tuple<_Args2...>&,
- _Index_tuple<_Indexes1...>, _Index_tuple<_Indexes2...>);
+#endif // lib concepts
#else
// C++03 implementation
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
index d300292b463..a3e6a452583 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
@@ -16,7 +16,7 @@
// <http://www.gnu.org/licenses/>.
// { dg-options "-Wdeprecated" }
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && { ! c++20 } } } }
#include <utility>
diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
index 508ca32ecb7..ecd5acf9375 100644
--- a/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc
@@ -83,12 +83,12 @@ void f7(std::pair<long, long>) {}
std::pair<ExplicitDefault, int> f8()
{
- return {}; // { dg-error "could not convert" }
+ return {}; // { dg-error "convert" }
}
std::pair<ExplicitDefaultDefault, int> f9()
{
- return {}; // { dg-error "could not convert" }
+ return {}; // { dg-error "convert" }
}
void f10(std::pair<ExplicitDefault, int>) {}
@@ -107,8 +107,8 @@ void test_arg_passing()
f7({1,2});
f7(std::pair<int, int>{});
f7(std::pair<long, long>{});
- f10({}); // { dg-error "could not convert" }
- f11({}); // { dg-error "could not convert" }
+ f10({}); // { dg-error "convert" }
+ f11({}); // { dg-error "convert" }
f10(std::pair<ExplicitDefault, int>{});
f11(std::pair<ExplicitDefaultDefault, int>{});
}