diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2021-04-28 17:46:01 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2021-04-28 17:56:51 +0100 |
commit | e1543e694dadf1ea70eb72325219bc0cdc914a35 (patch) | |
tree | 36e3876b1d3f5e82b548f7906df902ee1198d902 | |
parent | d96db15967e78d7cecea3b1cf3169ceb924678ac (diff) | |
download | gcc-e1543e694dadf1ea70eb72325219bc0cdc914a35.zip gcc-e1543e694dadf1ea70eb72325219bc0cdc914a35.tar.gz gcc-e1543e694dadf1ea70eb72325219bc0cdc914a35.tar.bz2 |
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) [__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.
-rw-r--r-- | libstdc++-v3/include/bits/stl_pair.h | 221 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/pair/cons/99957.cc | 2 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/pair/cons/explicit_construct.cc | 8 |
3 files changed, 193 insertions, 38 deletions
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h index 883d744..c89f377 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,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 diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc index d75ff21..150bcd5 100644 --- a/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc +++ b/libstdc++-v3/testsuite/20_util/pair/cons/99957.cc @@ -1,5 +1,5 @@ // { 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 508ca32..ecd5acf 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>{}); } |