]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Simplify std::pair constraints using concepts
authorJonathan Wakely <jwakely@redhat.com>
Wed, 28 Apr 2021 16:46:01 +0000 (17:46 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 28 Apr 2021 16:56:51 +0000 (17:56 +0100)
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) [__cplusplus > 202002]: Add
new definitions for constructors and assignment operators using
concepts for constraints.
* 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.

libstdc++-v3/include/bits/stl_pair.h
libstdc++-v3/testsuite/20_util/pair/cons/99957.cc
libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc

index 883d7441b3d28b1cf3f498949e290e2eef7a4d82..c89f377fddc504736d2cf70389462aa314d974c3 100644 (file)
@@ -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,11 +560,7 @@ _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&
+      pair&
       operator=(typename conditional<
                __and_<is_copy_assignable<_T1>,
                       is_copy_assignable<_T2>>::value,
@@ -397,7 +571,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return *this;
       }
 
-      _GLIBCXX20_CONSTEXPR pair&
+      pair&
       operator=(typename conditional<
                __and_<is_move_assignable<_T1>,
                       is_move_assignable<_T2>>::value,
@@ -411,7 +585,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       template<typename _U1, typename _U2>
-       _GLIBCXX20_CONSTEXPR
        typename enable_if<__and_<is_assignable<_T1&, const _U1&>,
                                  is_assignable<_T2&, const _U2&>>::value,
                           pair&>::type
@@ -423,7 +596,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _U1, typename _U2>
-       _GLIBCXX20_CONSTEXPR
        typename enable_if<__and_<is_assignable<_T1&, _U1&&>,
                                  is_assignable<_T2&, _U2&&>>::value,
                           pair&>::type
@@ -433,24 +605,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
 
index d75ff21da60be657f98be286466c6b8a685a0868..150bcd57c9a8a8a42dcb401ed6601f5702a9b48c 100644 (file)
@@ -1,5 +1,5 @@
 // { dg-options "-Wdeprecated" }
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && { ! c++20 } } } }
 
 #include <utility>
 
index 508ca32ecb7b1bc34540ef832209c201adb952ea..ecd5acf93752637e7a7485adba01a683ccc52355 100644 (file)
@@ -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>{});
 }