* [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple
@ 2022-08-23 1:34 Patrick Palka
2022-08-23 1:34 ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Patrick Palka
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Patrick Palka @ 2022-08-23 1:34 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
P2321R2 adds new conditionally explicit constructors to std::tuple which
we'll concisely implement in a subsequent patch using explicit(bool), like
in our C++20 std::pair implementation. But before we can do that, this
patch first adds members to _TupleConstraints that test for constructibility
and convertibility separately; we'll use the first in the new constructors'
constraints, and the second in their explicit specifier.
In passing, this patch also redefines the existing predicates
__is_ex/implicitly_constructible in terms of these new members. This
seems to reduce compile time and memory usage by about 10% for large
tuples when using the relevant constructors constrained by
_Explicit/_ImplicitCtor (since we no longer have to redundantly expand
and process is_constructible<_Types, _UTypes>... twice for each pair of
such constructors). In order to retain maximal short circuiting, do
this only when constexpr if is available.
Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
libstdc++-v3/ChangeLog:
* include/std/tuple (_TupleConstraints::__convertible): Define
for C++17.
(_TupleConstraints::__constructible): Likewise.
(_TupleConstraints::__is_explicitly_constructible): For C++17
define this in terms of __convertible and __constructible
using constexpr if.
(_TupleConstraints::__is_implicitly_constructible): Likewise.
---
libstdc++-v3/include/std/tuple | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 6d0060a191c..d0c168fd7e2 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -553,15 +553,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<bool, typename... _Types>
struct _TupleConstraints
{
+#if __cplusplus >= 201703L
+ template<typename... _UTypes>
+ static constexpr bool __constructible
+ = __and_v<is_constructible<_Types, _UTypes>...>;
+
+ template<typename... _UTypes>
+ static constexpr bool __convertible
+ = __and_v<is_convertible<_UTypes, _Types>...>;
+#endif
+
// Constraint for a non-explicit constructor.
// True iff each Ti in _Types... can be constructed from Ui in _UTypes...
// and every Ui is implicitly convertible to Ti.
template<typename... _UTypes>
static constexpr bool __is_implicitly_constructible()
{
+#if __cplusplus >= 201703L
+ if constexpr (__constructible<_UTypes...>)
+ return __convertible<_UTypes...>;
+ return false;
+#else
return __and_<is_constructible<_Types, _UTypes>...,
is_convertible<_UTypes, _Types>...
>::value;
+#endif
}
// Constraint for a non-explicit constructor.
@@ -570,9 +586,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _UTypes>
static constexpr bool __is_explicitly_constructible()
{
+#if __cplusplus >= 201703L
+ if constexpr (__constructible<_UTypes...>)
+ return !__convertible<_UTypes...>;
+ return false;
+#else
return __and_<is_constructible<_Types, _UTypes>...,
__not_<__and_<is_convertible<_UTypes, _Types>...>>
>::value;
+#endif
}
static constexpr bool __is_implicitly_default_constructible()
--
2.37.2.382.g795ea8776b
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2
2022-08-23 1:34 [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Patrick Palka
@ 2022-08-23 1:34 ` Patrick Palka
2022-08-23 12:03 ` Jonathan Wakely
2022-08-23 1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
2022-08-23 9:15 ` [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Jonathan Wakely
2 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2022-08-23 1:34 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
This implements the non-<ranges> changes from P2321R2, which primarily
consist of new converting constructors, assignment operator and swap
overloads for std::pair and std::tuple.
Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
libstdc++-v3/ChangeLog:
* include/bits/stl_bvector.h (_Bit_reference::operator=): Define
const overload for C++23 as per P2321R2.
* include/bits/stl_pair.h (pair::swap): Likewise.
(pair::pair): Define additional converting constructors for
C++23 as per P2321R2.
(pair::operator=): Define const overloads for C++23 as per
P2321R2.
(swap): Define overload taking const pair& for C++23 as per
P2321R2.
(basic_common_reference): Define partial specialization for
pair for C++23 as per P2321R2.
(common_type): Likewise.
* include/bits/uses_allocator_args.h
(uses_allocator_construction_args): Define additional pair
overloads for C++23 as per P2321R2.
* include/std/tuple (_Tuple_impl::_Tuple_impl): Define
additional converting constructors for C++23 as per P2321R2.
(_Tuple_impl::_M_assign): Define const overloads for C++23
as per P2321R2.
(_Tuple_impl::_M_swap): Likewise.
(tuple::__constructible): Define as a convenient renaming of
_TCC<true>::__constructible.
(tuple::__convertible): As above but for _TCC<true>::__convertible.
(tuple::tuple): Define additional converting constructors for
C++23 as per P2321R2.
(tuple::operator=): Define const overloads for C++23 as per
P2321R2.
(tuple::swap): Likewise.
(basic_common_reference): Define partial specialization for
tuple for C++23 as per P2321R2.
(common_type): Likewise.
* testsuite/20_util/pair/p2321.cc: New test.
* testsuite/20_util/tuple/p2321.cc: New test.
* testsuite/23_containers/vector/bool/element_access/1.cc: New test.
---
libstdc++-v3/include/bits/stl_bvector.h | 12 +
libstdc++-v3/include/bits/stl_pair.h | 118 +++-
.../include/bits/uses_allocator_args.h | 41 ++
libstdc++-v3/include/std/tuple | 416 +++++++++++
libstdc++-v3/testsuite/20_util/pair/p2321.cc | 208 ++++++
libstdc++-v3/testsuite/20_util/tuple/p2321.cc | 664 ++++++++++++++++++
.../vector/bool/element_access/1.cc | 26 +
7 files changed, 1480 insertions(+), 5 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2321.cc
create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2321.cc
create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc
diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index d256af40f40..c5fd19e7309 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -106,6 +106,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const _Bit_reference&
+ operator=(bool __x) const noexcept
+ {
+ if (__x)
+ *_M_p |= _M_mask;
+ else
+ *_M_p &= ~_M_mask;
+ return *this;
+ }
+#endif
+
_GLIBCXX20_CONSTEXPR
_Bit_reference&
operator=(const _Bit_reference& __x) _GLIBCXX_NOEXCEPT
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 831e770d54b..d0efba635bc 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -212,6 +212,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(second, __p.second);
}
+#if __cplusplus > 202002L
+ /// Swap the first members and then the second members.
+ constexpr void
+ swap(const pair& __p) const
+ noexcept(__and_<__is_nothrow_swappable<const _T1>,
+ __is_nothrow_swappable<const _T2>>::value)
+ {
+ using std::swap;
+ swap(first, __p.first);
+ swap(second, __p.second);
+ }
+#endif // C++23
+
private:
template<typename... _Args1, size_t... _Indexes1,
typename... _Args2, size_t... _Indexes2>
@@ -283,7 +296,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
{ }
- /// Converting constructor from a `pair<U1, U2>` lvalue
+ /// Converting constructor from a const `pair<U1, U2>` lvalue
template<typename _U1, typename _U2>
requires (_S_constructible<const _U1&, const _U2&>())
constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
@@ -292,7 +305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: first(__p.first), second(__p.second)
{ }
- /// Converting constructor from a `pair<U1, U2>` rvalue
+ /// Converting constructor from a non-const `pair<U1, U2>` rvalue
template<typename _U1, typename _U2>
requires (_S_constructible<_U1, _U2>())
constexpr explicit(!_S_convertible<_U1, _U2>())
@@ -302,6 +315,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second(std::forward<_U2>(__p.second))
{ }
+#if __cplusplus > 202002L
+ /// Converting constructor from a non-const `pair<U1, U2>` lvalue
+ 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(__p.first), second(__p.second)
+ { }
+
+ /// Converting constructor from a const `pair<U1, U2>` rvalue
+ 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(std::forward<const _U1>(__p.first)),
+ second(std::forward<const _U2>(__p.second))
+ { }
+#endif
+
private:
/// @cond undocumented
template<typename _U1, typename _U2>
@@ -349,7 +383,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
- /// Converting assignment from a `pair<U1, U2>` lvalue
+ /// Converting assignment from a const `pair<U1, U2>` lvalue
template<typename _U1, typename _U2>
constexpr pair&
operator=(const pair<_U1, _U2>& __p)
@@ -361,7 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
- /// Converting assignment from a `pair<U1, U2>` rvalue
+ /// Converting assignment from a non-const `pair<U1, U2>` rvalue
template<typename _U1, typename _U2>
constexpr pair&
operator=(pair<_U1, _U2>&& __p)
@@ -372,7 +406,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
second = std::forward<_U2>(__p.second);
return *this;
}
-#else
+
+#if __cplusplus > 202002L
+ /// Copy assignment operator
+ constexpr const pair&
+ operator=(const pair& __p) const
+ requires is_copy_assignable_v<const first_type>
+ && is_copy_assignable_v<const second_type>
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Move assignment operator
+ constexpr const pair&
+ operator=(pair&& __p) const
+ requires is_assignable_v<const first_type&, first_type>
+ && is_assignable_v<const second_type&, second_type>
+ {
+ first = std::forward<first_type>(__p.first);
+ second = std::forward<second_type>(__p.second);
+ return *this;
+ }
+
+ /// Converting assignment from a const `pair<U1, U2>` lvalue
+ template<typename _U1, typename _U2>
+ constexpr const pair&
+ operator=(const pair<_U1, _U2>& __p) const
+ requires is_assignable_v<const first_type&, const _U1&>
+ && is_assignable_v<const second_type&, const _U2&>
+ {
+ first = __p.first;
+ second = __p.second;
+ return *this;
+ }
+
+ /// Converting assignment from a non-const `pair<U1, U2>` rvalue
+ template<typename _U1, typename _U2>
+ constexpr const pair&
+ operator=(pair<_U1, _U2>&& __p) const
+ requires is_assignable_v<const first_type&, _U1>
+ && is_assignable_v<const second_type&, _U2>
+ {
+ first = std::forward<_U1>(__p.first);
+ second = std::forward<_U2>(__p.second);
+ return *this;
+ }
+#endif // C++23
+#else // !__cpp_lib_concepts
// C++11/14/17 implementation using enable_if, partially constexpr.
/** The default constructor creates @c first and @c second using their
@@ -710,6 +792,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
+#if __cplusplus > 202002L
+ template<typename _T1, typename _T2>
+ requires is_swappable<const _T1>::value && is_swappable<const _T2>::value
+ constexpr void
+ swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+ noexcept(noexcept(__x.swap(__y)))
+ { __x.swap(__y); }
+#endif // C++23
+
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
template<typename _T1, typename _T2>
typename enable_if<!__and_<__is_swappable<_T1>,
@@ -918,6 +1009,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
get(const pair<_Up, _Tp>&& __p) noexcept
{ return std::move(__p.second); }
+#if __cplusplus > 202002L
+ template<typename _T1, typename _T2, typename _U1, typename _U2,
+ template<typename> class _TQual, template<typename> class _UQual>
+ requires requires { typename pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+ common_reference_t<_TQual<_T2>, _UQual<_U2>>>; }
+ struct basic_common_reference<pair<_T1, _T2>, pair<_U1, _U2>, _TQual, _UQual>
+ {
+ using type = pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+ common_reference_t<_TQual<_T2>, _UQual<_U2>>>;
+ };
+
+ template<typename _T1, typename _T2, typename _U1, typename _U2>
+ requires requires { typename pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; }
+ struct common_type<pair<_T1, _T2>, pair<_U1, _U2>>
+ { using type = pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; };
+#endif
+
#endif // C++14
/// @}
#endif // C++11
diff --git a/libstdc++-v3/include/bits/uses_allocator_args.h b/libstdc++-v3/include/bits/uses_allocator_args.h
index 09cdbf1aaa8..3528e4cc4fa 100644
--- a/libstdc++-v3/include/bits/uses_allocator_args.h
+++ b/libstdc++-v3/include/bits/uses_allocator_args.h
@@ -107,6 +107,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr auto
uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept;
+#if __cplusplus > 202002L
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc&,
+ pair<_Up, _Vp>&) noexcept;
+
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc&, const pair<_Up, _Vp>&&) noexcept;
+#endif
+
template<_Std_pair _Tp, typename _Alloc, typename _Tuple1, typename _Tuple2>
constexpr auto
uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
@@ -181,6 +192,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::move(__pr).second));
}
+#if __cplusplus > 202002L
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc& __a,
+ pair<_Up, _Vp>& __pr) noexcept
+ {
+ using _Tp1 = typename _Tp::first_type;
+ using _Tp2 = typename _Tp::second_type;
+
+ return std::make_tuple(piecewise_construct,
+ std::uses_allocator_construction_args<_Tp1>(__a, __pr.first),
+ std::uses_allocator_construction_args<_Tp2>(__a, __pr.second));
+ }
+
+ template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+ constexpr auto
+ uses_allocator_construction_args(const _Alloc& __a,
+ const pair<_Up, _Vp>&& __pr) noexcept
+ {
+ using _Tp1 = typename _Tp::first_type;
+ using _Tp2 = typename _Tp::second_type;
+
+ return std::make_tuple(piecewise_construct,
+ std::uses_allocator_construction_args<_Tp1>(__a,
+ std::move(__pr).first),
+ std::uses_allocator_construction_args<_Tp2>(__a,
+ std::move(__pr).second));
+ }
+#endif
+
template<typename _Tp, typename _Alloc, typename... _Args>
constexpr _Tp
make_obj_using_allocator(const _Alloc& __a, _Args&&... __args)
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index d0c168fd7e2..812e70d17be 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -316,6 +316,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ constexpr
+ _Tuple_impl(_Tuple_impl<_Idx, _UElements...>& __in)
+ : _Inherited(_Tuple_impl<_Idx, _UElements...>::_M_tail(__in)),
+ _Base(_Tuple_impl<_Idx, _UElements...>::_M_head(__in))
+ { }
+
+ template<typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+ : _Inherited(std::move
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+ _Base(std::forward<const _UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+ { }
+#endif
+
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -379,6 +397,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ _Tuple_impl<_Idx, _UHead, _UTails...>& __in)
+ : _Inherited(__tag, __a,
+ _Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)),
+ _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+ _Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in))
+ { }
+
+ template<typename _Alloc, typename _UHead, typename... _UTails>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+ : _Inherited(__tag, __a, std::move
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+ _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+ std::forward<const _UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+ { }
+#endif
+
template<typename... _UElements>
_GLIBCXX20_CONSTEXPR
void
@@ -400,6 +441,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
}
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ constexpr void
+ _M_assign(const _Tuple_impl<_Idx, _UElements...>& __in) const
+ {
+ _M_head(*this) = _Tuple_impl<_Idx, _UElements...>::_M_head(__in);
+ _M_tail(*this)._M_assign(
+ _Tuple_impl<_Idx, _UElements...>::_M_tail(__in));
+ }
+
+ template<typename _UHead, typename... _UTails>
+ constexpr void
+ _M_assign(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) const
+ {
+ _M_head(*this) = std::forward<_UHead>
+ (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in));
+ _M_tail(*this)._M_assign(
+ std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
+ }
+#endif
+
protected:
_GLIBCXX20_CONSTEXPR
void
@@ -409,6 +471,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(_M_head(*this), _M_head(__in));
_Inherited::_M_swap(_M_tail(__in));
}
+
+#if __cplusplus > 202002L
+ constexpr void
+ _M_swap(const _Tuple_impl& __in) const
+ {
+ using std::swap;
+ swap(_M_head(*this), _M_head(__in));
+ _Inherited::_M_swap(_M_tail(__in));
+ }
+#endif
};
// Basis case of inheritance recursion.
@@ -469,6 +541,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _UHead>
+ constexpr
+ _Tuple_impl(_Tuple_impl<_Idx, _UHead>& __in)
+ : _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in))
+ { }
+
+ template<typename _UHead>
+ constexpr
+ _Tuple_impl(const _Tuple_impl<_Idx, _UHead>&& __in)
+ : _Base(std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+ { }
+#endif
+
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -521,6 +607,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _UHead>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ _Tuple_impl<_Idx, _UHead>& __in)
+ : _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+ _Tuple_impl<_Idx, _UHead>::_M_head(__in))
+ { }
+
+ template<typename _Alloc, typename _UHead>
+ constexpr
+ _Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+ const _Tuple_impl<_Idx, _UHead>&& __in)
+ : _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+ std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+ { }
+#endif
+
template<typename _UHead>
_GLIBCXX20_CONSTEXPR
void
@@ -538,6 +642,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
= std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
}
+#if __cplusplus > 202002L
+ template<typename _UHead>
+ constexpr void
+ _M_assign(const _Tuple_impl<_Idx, _UHead>& __in) const
+ {
+ _M_head(*this) = _Tuple_impl<_Idx, _UHead>::_M_head(__in);
+ }
+
+ template<typename _UHead>
+ constexpr void
+ _M_assign(_Tuple_impl<_Idx, _UHead>&& __in) const
+ {
+ _M_head(*this)
+ = std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
+ }
+#endif
+
protected:
_GLIBCXX20_CONSTEXPR
void
@@ -546,6 +667,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using std::swap;
swap(_M_head(*this), _M_head(__in));
}
+
+#if __cplusplus > 202002L
+ constexpr void
+ _M_swap(const _Tuple_impl& __in) const
+ {
+ using std::swap;
+ swap(_M_head(*this), _M_head(__in));
+ }
+#endif
};
// Concept utility functions, reused in conditionally-explicit
@@ -728,6 +858,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static constexpr bool __use_other_ctor()
{ return _UseOtherCtor<_Tuple>::value; }
+#if __cplusplus > 202002L
+ template<typename... _Args>
+ static constexpr bool __constructible
+ = _TCC<true>::template __constructible<_Args...>;
+
+ template<typename... _Args>
+ static constexpr bool __convertible
+ = _TCC<true>::template __convertible<_Args...>;
+#endif
+
public:
template<typename _Dummy = void,
_ImplicitDefaultCtor<is_void<_Dummy>::value> = true>
@@ -815,6 +955,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(__nothrow_constructible<_UElements...>())
: _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
+#if __cplusplus > 202002L
+ template<typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<tuple<_UElements...>&>())
+ && __constructible<_UElements&...>
+ explicit(!__convertible<_UElements&...>)
+ constexpr
+ tuple(tuple<_UElements...>& __in)
+ noexcept(__nothrow_constructible<_UElements&...>())
+ : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+ { }
+
+ template<typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<const tuple<_UElements...>&&>())
+ && __constructible<const _UElements...>
+ explicit(!__convertible<const _UElements...>)
+ constexpr
+ tuple(const tuple<_UElements...>&& __in)
+ noexcept(__nothrow_constructible<const _UElements...>())
+ : _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&&>(__in)) { }
+#endif
+
// Allocator-extended constructors.
template<typename _Alloc,
@@ -913,6 +1076,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<tuple<_UElements...>&>())
+ && __constructible<_UElements&...>
+ explicit(!__convertible<_UElements&...>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ tuple<_UElements...>& __in)
+ : _Inherited(__tag, __a,
+ static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+ { }
+
+ template<typename _Alloc, typename... _UElements>
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (!__use_other_ctor<const tuple<_UElements...>>())
+ && __constructible<const _UElements...>
+ explicit(!__convertible<const _UElements...>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ const tuple<_UElements...>&& __in)
+ : _Inherited(__tag, __a,
+ static_cast<const _Tuple_impl<0, _UElements...>&&>(__in))
+ { }
+#endif
+
// tuple assignment
_GLIBCXX20_CONSTEXPR
@@ -957,12 +1146,57 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const tuple&
+ operator=(const tuple& __in) const
+ requires (is_copy_assignable_v<const _Elements> && ...)
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ constexpr const tuple&
+ operator=(tuple&& __in) const
+ requires (is_assignable_v<const _Elements&, _Elements> && ...)
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+
+ template<typename... _UElements>
+ constexpr const tuple&
+ operator=(const tuple<_UElements...>& __in) const
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (is_assignable_v<const _Elements&, const _UElements&> && ...)
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ template<typename... _UElements>
+ constexpr const tuple&
+ operator=(tuple<_UElements...>&& __in) const
+ requires (sizeof...(_Elements) == sizeof...(_UElements))
+ && (is_assignable_v<const _Elements&, _UElements> && ...)
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+#endif
+
// tuple swap
_GLIBCXX20_CONSTEXPR
void
swap(tuple& __in)
noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
{ _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+ constexpr void
+ swap(const tuple& __in) const
+ noexcept(__and_<__is_nothrow_swappable<const _Elements>...>::value)
+ { _Inherited::_M_swap(__in); }
+#endif
};
#if __cpp_deduction_guides >= 201606
@@ -985,6 +1219,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
public:
_GLIBCXX20_CONSTEXPR
void swap(tuple&) noexcept { /* no-op */ }
+#if __cplusplus > 202002L
+ constexpr void swap(const tuple&) const noexcept { /* no-op */ }
+#endif
// We need the default since we're going to define no-op
// allocator constructors.
tuple() = default;
@@ -1064,6 +1301,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static constexpr bool __is_alloc_arg()
{ return is_same<__remove_cvref_t<_U1>, allocator_arg_t>::value; }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ static constexpr bool __constructible
+ = _TCC<true>::template __constructible<_U1, _U2>;
+
+ template<typename _U1, typename _U2>
+ static constexpr bool __convertible
+ = _TCC<true>::template __convertible<_U1, _U2>;
+#endif
+
public:
template<bool _Dummy = true,
_ImplicitDefaultCtor<_Dummy, _T1, _T2> = true>
@@ -1139,6 +1386,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(__nothrow_constructible<_U1, _U2>())
: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(tuple<_U1, _U2>& __in)
+ noexcept(__nothrow_constructible<_U1&, _U2&>())
+ : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) { }
+
+ template<typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(const tuple<_U1, _U2>&& __in)
+ noexcept(__nothrow_constructible<const _U1, const _U2>())
+ : _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in)) { }
+#endif
+
template<typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
constexpr
@@ -1169,6 +1434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(std::forward<_U1>(__in.first),
std::forward<_U2>(__in.second)) { }
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(pair<_U1, _U2>& __in)
+ noexcept(__nothrow_constructible<_U1&, _U2&>())
+ : _Inherited(__in.first, __in.second) { }
+
+ template<typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(const pair<_U1, _U2>&& __in)
+ noexcept(__nothrow_constructible<const _U1, const _U2>())
+ : _Inherited(std::forward<const _U1>(__in.first),
+ std::forward<const _U2>(__in.second)) { }
+#endif
+
// Allocator-extended constructors.
template<typename _Alloc,
@@ -1252,6 +1536,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
{ }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ tuple<_U1, _U2>& __in)
+ : _Inherited(__tag, __a,
+ static_cast<_Tuple_impl<0, _U1, _U2>&>(__in))
+ { }
+
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ const tuple<_U1, _U2>&& __in)
+ : _Inherited(__tag, __a,
+ static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in))
+ { }
+#endif
+
template<typename _Alloc, typename _U1, typename _U2,
_ImplicitCtor<true, const _U1&, const _U2&> = true>
_GLIBCXX20_CONSTEXPR
@@ -1282,6 +1588,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
std::forward<_U2>(__in.second)) { }
+#if __cplusplus > 202002L
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<_U1&, _U2&>
+ explicit(!__convertible<_U1&, _U2&>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a,
+ pair<_U1, _U2>& __in)
+ : _Inherited(__tag, __a, __in.first, __in.second) { }
+
+ template<typename _Alloc, typename _U1, typename _U2>
+ requires __constructible<const _U1, const _U2>
+ explicit(!__convertible<const _U1, const _U2>)
+ constexpr
+ tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>&& __in)
+ : _Inherited(__tag, __a, std::forward<const _U1>(__in.first),
+ std::forward<const _U2>(__in.second)) { }
+#endif
+
// Tuple assignment.
_GLIBCXX20_CONSTEXPR
@@ -1326,6 +1650,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ constexpr const tuple&
+ operator=(const tuple& __in) const
+ requires is_copy_assignable_v<const _T1> && is_copy_assignable_v<const _T2>
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ constexpr const tuple&
+ operator=(tuple&& __in) const
+ requires is_assignable_v<const _T1&, _T1> && is_assignable_v<const _T2, _T2>
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(const tuple<_U1, _U2>& __in) const
+ requires is_assignable_v<const _T1&, const _U1&>
+ && is_assignable_v<const _T2&, const _U2&>
+ {
+ this->_M_assign(__in);
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(tuple<_U1, _U2>&& __in) const
+ requires is_assignable_v<const _T1&, _U1>
+ && is_assignable_v<const _T2&, _U2>
+ {
+ this->_M_assign(std::move(__in));
+ return *this;
+ }
+#endif
+
template<typename _U1, typename _U2>
_GLIBCXX20_CONSTEXPR
__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
@@ -1348,12 +1710,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return *this;
}
+#if __cplusplus > 202002L
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(const pair<_U1, _U2>& __in) const
+ requires is_assignable_v<const _T1&, const _U1&>
+ && is_assignable_v<const _T2&, const _U2&>
+ {
+ this->_M_head(*this) = __in.first;
+ this->_M_tail(*this)._M_head(*this) = __in.second;
+ return *this;
+ }
+
+ template<typename _U1, typename _U2>
+ constexpr const tuple&
+ operator=(pair<_U1, _U2>&& __in) const
+ requires is_assignable_v<const _T1&, _U1>
+ && is_assignable_v<const _T2&, _U2>
+ {
+ this->_M_head(*this) = std::forward<_U1>(__in.first);
+ this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
+ return *this;
+ }
+#endif
+
_GLIBCXX20_CONSTEXPR
void
swap(tuple& __in)
noexcept(__and_<__is_nothrow_swappable<_T1>,
__is_nothrow_swappable<_T2>>::value)
{ _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+ constexpr void
+ swap(const tuple& __in) const
+ noexcept(__and_<__is_nothrow_swappable<const _T1>,
+ __is_nothrow_swappable<const _T2>>::value)
+ { _Inherited::_M_swap(__in); }
+#endif
};
@@ -1781,6 +2175,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
noexcept(noexcept(__x.swap(__y)))
{ __x.swap(__y); }
+#if __cplusplus > 202002L
+ template<typename... _Elements>
+ requires (__is_swappable<const _Elements>::value && ...)
+ constexpr void
+ swap(const tuple<_Elements...>& __x, const tuple<_Elements...>& __y)
+ noexcept(noexcept(__x.swap(__y)))
+ { __x.swap(__y); }
+#endif
+
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
template<typename... _Elements>
_GLIBCXX20_CONSTEXPR
@@ -1905,6 +2308,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif // C++17
+#if __cplusplus > 202002L
+ template<typename... _TTypes, typename... _UTypes,
+ template<typename> class TQual, template<typename> class UQual>
+ requires requires { typename tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; }
+ struct basic_common_reference<tuple<_TTypes...>, tuple<_UTypes...>, TQual, UQual>
+ { using type = tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; };
+
+ template<typename... _TTypes, typename... _UTypes>
+ requires requires { typename tuple<common_type_t<_TTypes, _UTypes>...>; }
+ struct common_type<tuple<_TTypes...>, tuple<_UTypes...>>
+ { using type = tuple<common_type_t<_TTypes, _UTypes>...>; };
+#endif
+
/// @}
_GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/20_util/pair/p2321.cc b/libstdc++-v3/testsuite/20_util/pair/p2321.cc
new file mode 100644
index 00000000000..4f436ee03d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/p2321.cc
@@ -0,0 +1,208 @@
+// Verify P2321R2 "zip" enhancements to std::pair.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <utility>
+#include <testsuite_hooks.h>
+
+using std::pair;
+
+struct A { };
+
+constexpr bool
+test01()
+{
+ struct B { bool v; constexpr B(A&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(false) pair(pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ pair<B, int> p2b0 = p2a0;
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ pair<int, B> p2b1 = p2a1;
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test02()
+{
+ struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(true) pair(pair<U1, U2>&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, pair<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, pair<int, B>>);
+
+ pair<A, int> p2a0;
+ pair<B, int> p2b0(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ pair<int, B> p2b1(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test03()
+{
+ struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(false) pair(const pair<U1, U2>&&);
+
+ const pair<A, int> p2a0;
+ pair<B, int> p2b0 = std::move(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ pair<int, B> p2b1 = std::move(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test04()
+{
+ struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+ // template<class U1, class U2>
+ // constexpr explicit(true) pair(const pair<U1, U2>&&);
+
+ static_assert(!std::is_convertible_v<const pair<A, int>&&, pair<B, int>>);
+ static_assert(!std::is_convertible_v<const pair<int, A>&&, pair<int, B>>);
+
+ const pair<A, int> p2a0;
+ pair<B, int> p2b0(std::move(p2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ pair<int, B> p2b1(std::move(p2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+constexpr bool
+test05()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const A&) const { v = true; return *this; }
+ };
+
+ // template<class U1, class U2>
+ // constexpr const pair& operator=(const pair<U1, U2>&) const;
+
+ const pair<A, A> p2a;
+ const pair<B, B> p2b;
+ p2b = p2a;
+
+ return true;
+}
+
+constexpr bool
+test06()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(A&&) const { v = true; return *this; }
+ };
+
+ // template<class U1, class U2>
+ // constexpr const pair& operator=(pair<U1, U2>&&) const;
+
+ pair<A, A> p2a;
+ const pair<B, B> p2b;
+ p2b = std::move(p2a);
+
+ return true;
+}
+
+constexpr bool
+test07()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const B&) const { v = true; return *this; }
+ };
+
+ // constexpr const pair& operator=(const pair&) const;
+
+ const pair<B, B> t2a;
+ const pair<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ return true;
+}
+
+constexpr bool
+test08()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(B&&) const { v = true; return *this; }
+ };
+
+ // constexpr const pair& operator=(pair&&) const;
+
+ pair<B, B> t2a;
+ const pair<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ return true;
+}
+
+struct S
+{
+ mutable int v = 0;
+ friend constexpr void swap(S&& x, S&& y) = delete;
+ friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+ const pair<S, S> t2, u2;
+ std::swap(t2, u2);
+ VERIFY( std::get<0>(t2).v == 1 );
+ VERIFY( std::get<0>(u2).v == 1 );
+ VERIFY( std::get<1>(t2).v == 1 );
+ VERIFY( std::get<1>(u2).v == 1 );
+
+ static_assert(!std::is_swappable_v<const pair<A, int>&>);
+ static_assert(!std::is_swappable_v<const pair<int, A>&>);
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(test02());
+ static_assert(test03());
+ static_assert(test04());
+ // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+ test05();
+ test06();
+ test07();
+ test08();
+ test09();
+}
diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2321.cc b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc
new file mode 100644
index 00000000000..80fc23cf9d4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc
@@ -0,0 +1,664 @@
+// Verify P2321R2 "zip" enhancements to std::tuple.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <tuple>
+#include <memory>
+#include <testsuite_hooks.h>
+
+using std::tuple;
+using std::pair;
+using std::allocator;
+using std::allocator_arg_t;
+using std::allocator_arg;
+
+namespace alloc {
+ struct B01;
+ struct B02;
+ struct B03;
+ struct B04;
+}
+
+template<> struct std::uses_allocator<alloc::B01, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B02, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B03, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B04, allocator<int>> : std::true_type { };
+
+struct A { };
+
+constexpr bool
+test01()
+{
+ struct B { bool v; constexpr B(A&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = t2a0;
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = t2a1;
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = t3a0;
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = t3a1;
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = t3a2;
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(pair<UTypes...>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0 = p2a0;
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1 = p2a1;
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B01
+ {
+ bool v;
+ B01(A&);
+ constexpr B01(allocator_arg_t, allocator<int>, A&) : v(true) { }
+ };
+
+ constexpr bool
+ test01()
+ {
+ using B = B01;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b = {allocator_arg, allocator<int>{}, t1a};
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, t2a0};
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, t2a1};
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, t3a0};
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, t3a1};
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, t3a2};
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, p2a0};
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, p2a1};
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+}
+
+constexpr bool
+test02()
+{
+ struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(tuple<UTypes...>&);
+
+ static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+ tuple<A> t1a;
+ tuple<B> t1b(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0(t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1(t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(pair<UTypes...>&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B02
+ {
+ bool v;
+ explicit B02(A&);
+ explicit constexpr B02(allocator_arg_t, allocator<int>, A&) : v(true) { }
+ };
+
+ constexpr bool
+ test02()
+ {
+ using B = B02;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+ tuple<A> t1a;
+ tuple<B> t1b(allocator_arg, allocator<int>{}, t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, int> t2a0;
+ tuple<B, int> t2b0(allocator_arg, allocator<int>{}, t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ tuple<int, A> t2a1;
+ tuple<int, B> t2b1(allocator_arg, allocator<int>{}, t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+ pair<A, int> p2a0;
+ tuple<B, int> p2b0(allocator_arg, allocator<int>{}, p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ pair<int, A> p2a1;
+ tuple<int, B> p2b1(allocator_arg, allocator<int>{}, p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+} // namespace alloc
+
+constexpr bool
+test03()
+{
+ struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = std::move(t2a0);
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = std::move(t2a1);
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = std::move(t3a0);
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = std::move(t3a1);
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = std::move(t3a2);
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(false) tuple(const pair<UTypes...>&&);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0 = std::move(p2a0);
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1 = std::move(p2a1);
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B03
+ {
+ bool v;
+ B03(const A&&);
+ constexpr B03(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+ };
+
+ constexpr bool
+ test03()
+ {
+ using B = B03;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b = {allocator_arg, allocator<int>{}, std::move(t1a)};
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, std::move(t2a0)};
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, std::move(t2a1)};
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, std::move(t3a0)};
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, std::move(t3a1)};
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, std::move(t3a2)};
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(false)
+ // tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, std::move(p2a0)};
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, std::move(p2a1)};
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+};
+
+constexpr bool
+test04()
+{
+ struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(const tuple<UTypes...>&&);
+
+ static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+ const tuple<A> t1a;
+ tuple<B> t1b(std::move(t1a));
+ VERIFY( std::get<0>(t1b).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0(std::move(t2a0));
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1(std::move(t2a1));
+ VERIFY( std::get<1>(t2b1).v );
+
+ static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+ static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(std::move(t3a0));
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(std::move(t3a1));
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(std::move(t3a2));
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class... UTypes>
+ // constexpr explicit(true) tuple(const pair<UTypes...>&&);
+
+ static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+ static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+ const pair<A, int> p2a0;
+ tuple<B, int> p2b0(std::move(p2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ const pair<int, A> p2a1;
+ tuple<int, B> p2b1(std::move(p2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+}
+
+namespace alloc
+{
+ struct B04
+ {
+ bool v;
+ explicit B04(const A&&);
+ explicit constexpr B04(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+ };
+
+ constexpr bool
+ test04()
+ {
+ using B = B04;
+
+ // template<class Alloc, class... UTypes>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+ const tuple<A> t1a;
+ tuple<B> t1b(allocator_arg, allocator<int>{}, std::move(t1a));
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, int> t2a0;
+ tuple<B, int> t2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+ VERIFY( std::get<0>(t2b0).v );
+
+ const tuple<int, A> t2a1;
+ tuple<int, B> t2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+ VERIFY( std::get<1>(t2b1).v );
+
+ const tuple<A, int, int> t3a0;
+ tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, std::move(t3a0));
+ VERIFY( std::get<0>(t3b0).v );
+
+ const tuple<int, A, int> t3a1;
+ tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, std::move(t3a1));
+ VERIFY( std::get<1>(t3b1).v );
+
+ const tuple<int, int, A> t3a2;
+ tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, std::move(t3a2));
+ VERIFY( std::get<2>(t3b2).v );
+
+ // template<class Alloc, class U1, class U2>
+ // constexpr explicit(true)
+ // tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+ tuple<B, int> p2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+ VERIFY( std::get<0>(p2b0).v );
+
+ tuple<int, B> p2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+ VERIFY( std::get<1>(p2b1).v );
+
+ return true;
+ }
+};
+
+constexpr bool
+test05()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const A&) const { v = true; return *this; }
+ };
+
+ // template<class... UTypes>
+ // constexpr const tuple& operator=(const tuple<UTypes...>&) const;
+
+ const tuple<A> t1a;
+ const tuple<B> t1b;
+ t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<A, A> t2a;
+ const tuple<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ const tuple<A, A, A> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = t3a;
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ // template<class U1, class U2>
+ // constexpr const tuple& operator=(const pair<U1, U2>&) const;
+
+ const pair<A, A> p2a;
+ const tuple<B, B> p2b;
+ p2b = p2a;
+
+ return true;
+}
+
+constexpr bool
+test06()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(A&&) const { v = true; return *this; }
+ };
+
+ // template<class... UTypes>
+ // constexpr const tuple& operator=(tuple<UTypes...>&&) const;
+
+ tuple<A> t1a;
+ const tuple<B> t1b;
+ t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<A, A> t2a;
+ const tuple<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ tuple<A, A, A> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = std::move(t3a);
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ // template<class U1, class U2>
+ // constexpr const tuple& operator=(pair<U1, U2>&&) const;
+
+ pair<A, A> p2a;
+ const tuple<B, B> p2b;
+ p2b = std::move(p2a);
+
+ return true;
+}
+
+constexpr bool
+test07()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(const B&) const { v = true; return *this; }
+ };
+
+ // constexpr const tuple& operator=(const tuple&) const;
+
+ const tuple<B> t1a;
+ const tuple<B> t1b;
+ t1b = t1a;
+ VERIFY( std::get<0>(t1b).v );
+
+ const tuple<B, B> t2a;
+ const tuple<B, B> t2b;
+ t2b = t2a;
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ const tuple<B, B, B> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = t3a;
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ return true;
+}
+
+constexpr bool
+test08()
+{
+ struct B
+ {
+ mutable bool v;
+ constexpr const B& operator=(B&&) const { v = true; return *this; }
+ };
+
+ // constexpr const tuple& operator=(tuple&&) const;
+
+ tuple<B> t1a;
+ const tuple<B> t1b;
+ t1b = std::move(t1a);
+ VERIFY( std::get<0>(t1b).v );
+
+ tuple<B, B> t2a;
+ const tuple<B, B> t2b;
+ t2b = std::move(t2a);
+ VERIFY( std::get<0>(t2b).v );
+ VERIFY( std::get<1>(t2b).v );
+
+ tuple<B, B, B> t3a;
+ const tuple<B, B, B> t3b;
+ t3b = std::move(t3a);
+ VERIFY( std::get<0>(t3b).v );
+ VERIFY( std::get<1>(t3b).v );
+ VERIFY( std::get<2>(t3b).v );
+
+ return true;
+}
+
+struct S
+{
+ mutable int v = 0;
+ friend constexpr void swap(S&& x, S&& y) = delete;
+ friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+ const tuple<S> t1, u1;
+ std::swap(t1, u1);
+ VERIFY( std::get<0>(t1).v == 1 );
+ VERIFY( std::get<0>(u1).v == 1 );
+
+ const tuple<S, S> t2, u2;
+ std::swap(t2, u2);
+ VERIFY( std::get<0>(t2).v == 1 );
+ VERIFY( std::get<0>(u2).v == 1 );
+ VERIFY( std::get<1>(t2).v == 1 );
+ VERIFY( std::get<1>(u2).v == 1 );
+
+ const tuple<S, S, S> t3, u3;
+ std::swap(t3, u3);
+ VERIFY( std::get<0>(t3).v == 1 );
+ VERIFY( std::get<0>(u3).v == 1 );
+ VERIFY( std::get<1>(t3).v == 1 );
+ VERIFY( std::get<1>(u3).v == 1 );
+ VERIFY( std::get<2>(t3).v == 1 );
+ VERIFY( std::get<2>(u3).v == 1 );
+
+ static_assert(!std::is_swappable_v<const tuple<A>&>);
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(alloc::test01());
+ static_assert(test02());
+ static_assert(alloc::test02());
+ static_assert(test03());
+ static_assert(alloc::test03());
+ static_assert(test04());
+ static_assert(alloc::test04());
+ // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+ test05();
+ test06();
+ test07();
+ test08();
+ test09();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc
new file mode 100644
index 00000000000..9016c026b33
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc
@@ -0,0 +1,26 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+// { dg-xfail-if "not supported" { debug_mode } }
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+ // P2321R2
+ // constexpr const reference& vector<bool>::operator=(bool x) const noexcept;
+
+ std::vector<bool> v(1);
+ const auto e = v[0];
+ e = true;
+ VERIFY( v[0] );
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+}
--
2.37.2.382.g795ea8776b
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/3] libstdc++: Implement ranges::zip_view from P2321R2
2022-08-23 1:34 [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Patrick Palka
2022-08-23 1:34 ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Patrick Palka
@ 2022-08-23 1:35 ` Patrick Palka
2022-08-24 12:15 ` Jonathan Wakely
2022-08-26 20:05 ` Jonathan Wakely
2022-08-23 9:15 ` [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Jonathan Wakely
2 siblings, 2 replies; 11+ messages in thread
From: Patrick Palka @ 2022-08-23 1:35 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
Tested on 86_64-pc-linux-gnu, does this look OK for trunk?
libstdc++-v3/ChangeLog:
* include/bits/ranges_algo.h (__min_fn, min): Move to ...
* include/bits/ranges_util.h: ... here in order to avoid
including all of ranges_algo.h from <ranges>.
* include/std/ranges (__detail::__zip_is_common): Define for
C++23 as per P2321R2.
(__detail::__tuple_or_pair): Likewise.
(__detail::__tuple_or_pair_t): Likewise.
(__detail::__tuple_transform): Likewise.
(__detail::__tuple_for_each): Likewise.
(zip_view): Likewise.
(enable_borrowed_range<zip_view>): Likewise.
(__detail::__all_random_access): Likewise.
(__detail::__all_bidirectional): Likewise.
(__detail::__all_forward): Likewise.
(__detail::__zip_view_iter_cat): Likewise.
(zip_view::iterator): Likewise.
(zip_view::sentinel): Likewise.
(testsuite/std/ranges/zip/1.cc): New test.
---
libstdc++-v3/include/bits/ranges_algo.h | 54 +--
libstdc++-v3/include/bits/ranges_util.h | 55 +++
libstdc++-v3/include/std/ranges | 445 +++++++++++++++++++++
libstdc++-v3/testsuite/std/ranges/zip/1.cc | 101 +++++
4 files changed, 602 insertions(+), 53 deletions(-)
create mode 100644 libstdc++-v3/testsuite/std/ranges/zip/1.cc
diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index 3d30fb1428c..2a116361a67 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -2902,59 +2902,7 @@ namespace ranges
inline constexpr __set_symmetric_difference_fn set_symmetric_difference{};
- struct __min_fn
- {
- template<typename _Tp, typename _Proj = identity,
- indirect_strict_weak_order<projected<const _Tp*, _Proj>>
- _Comp = ranges::less>
- constexpr const _Tp&
- operator()(const _Tp& __a, const _Tp& __b,
- _Comp __comp = {}, _Proj __proj = {}) const
- {
- if (std::__invoke(__comp,
- std::__invoke(__proj, __b),
- std::__invoke(__proj, __a)))
- return __b;
- else
- return __a;
- }
-
- template<input_range _Range, typename _Proj = identity,
- indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
- _Comp = ranges::less>
- requires indirectly_copyable_storable<iterator_t<_Range>,
- range_value_t<_Range>*>
- constexpr range_value_t<_Range>
- operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const
- {
- auto __first = ranges::begin(__r);
- auto __last = ranges::end(__r);
- __glibcxx_assert(__first != __last);
- auto __result = *__first;
- while (++__first != __last)
- {
- auto __tmp = *__first;
- if (std::__invoke(__comp,
- std::__invoke(__proj, __tmp),
- std::__invoke(__proj, __result)))
- __result = std::move(__tmp);
- }
- return __result;
- }
-
- template<copyable _Tp, typename _Proj = identity,
- indirect_strict_weak_order<projected<const _Tp*, _Proj>>
- _Comp = ranges::less>
- constexpr _Tp
- operator()(initializer_list<_Tp> __r,
- _Comp __comp = {}, _Proj __proj = {}) const
- {
- return (*this)(ranges::subrange(__r),
- std::move(__comp), std::move(__proj));
- }
- };
-
- inline constexpr __min_fn min{};
+ // min is defined in <bits/ranges_util.h>.
struct __max_fn
{
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index 37d7bc862f9..bb56deee01b 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -649,6 +649,61 @@ namespace ranges
};
inline constexpr __search_fn search{};
+
+ struct __min_fn
+ {
+ template<typename _Tp, typename _Proj = identity,
+ indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+ _Comp = ranges::less>
+ constexpr const _Tp&
+ operator()(const _Tp& __a, const _Tp& __b,
+ _Comp __comp = {}, _Proj __proj = {}) const
+ {
+ if (std::__invoke(__comp,
+ std::__invoke(__proj, __b),
+ std::__invoke(__proj, __a)))
+ return __b;
+ else
+ return __a;
+ }
+
+ template<input_range _Range, typename _Proj = identity,
+ indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>>
+ _Comp = ranges::less>
+ requires indirectly_copyable_storable<iterator_t<_Range>,
+ range_value_t<_Range>*>
+ constexpr range_value_t<_Range>
+ operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const
+ {
+ auto __first = ranges::begin(__r);
+ auto __last = ranges::end(__r);
+ __glibcxx_assert(__first != __last);
+ auto __result = *__first;
+ while (++__first != __last)
+ {
+ auto __tmp = *__first;
+ if (std::__invoke(__comp,
+ std::__invoke(__proj, __tmp),
+ std::__invoke(__proj, __result)))
+ __result = std::move(__tmp);
+ }
+ return __result;
+ }
+
+ template<copyable _Tp, typename _Proj = identity,
+ indirect_strict_weak_order<projected<const _Tp*, _Proj>>
+ _Comp = ranges::less>
+ constexpr _Tp
+ operator()(initializer_list<_Tp> __r,
+ _Comp __comp = {}, _Proj __proj = {}) const
+ {
+ return (*this)(ranges::subrange(__r),
+ std::move(__comp), std::move(__proj));
+ }
+ };
+
+ inline constexpr __min_fn min{};
+
} // namespace ranges
using ranges::get;
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 3e71ecb32b7..8d0c2a52b40 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -39,6 +39,7 @@
#if __cpp_lib_concepts
#include <compare>
+#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <optional>
@@ -4352,6 +4353,450 @@ namespace views::__adaptor
inline constexpr auto values = elements<1>;
} // namespace views
+#if __cplusplus > 202002L
+ namespace __detail
+ {
+ template<typename... _Rs>
+ concept __zip_is_common = (sizeof...(_Rs) == 1 && (common_range<_Rs> && ...))
+ || (!(bidirectional_range<_Rs> && ...) && (common_range<_Rs> && ...))
+ || ((random_access_range<_Rs> && ...) && (sized_range<_Rs> && ...));
+
+ template<typename... _Ts>
+ struct __tuple_or_pair
+ { using type = std::tuple<_Ts...>; };
+
+ template<typename _Tp, typename _Up>
+ struct __tuple_or_pair<_Tp, _Up>
+ { using type = pair<_Tp, _Up>; };
+
+ template<typename... _Ts>
+ using __tuple_or_pair_t = typename __tuple_or_pair<_Ts...>::type;
+
+ template<typename _Fp, typename _Tuple>
+ constexpr auto
+ __tuple_transform(_Fp&& __f, _Tuple&& __tuple)
+ {
+ return std::apply([&]<typename... _Ts>(_Ts&&... __elts) {
+ return __tuple_or_pair_t<invoke_result_t<_Fp&, _Ts>...>
+ (std::__invoke(__f, std::forward<_Ts>(__elts))...);
+ }, std::forward<_Tuple>(__tuple));
+ }
+
+ template<typename _Fp, typename _Tuple>
+ constexpr void
+ __tuple_for_each(_Fp&& __f, _Tuple&& __tuple)
+ {
+ std::apply([&]<typename... _Ts>(_Ts&&... __elts) {
+ (std::__invoke(__f, std::forward<_Ts>(__elts)), ...);
+ }, std::forward<_Tuple>(__tuple));
+ }
+ } // namespace __detail
+
+ template<input_range... _Vs>
+ requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0)
+ class zip_view : public view_interface<zip_view<_Vs...>>
+ {
+ tuple<_Vs...> _M_views;
+
+ template<bool> class iterator;
+ template<bool> class sentinel;
+
+ public:
+ zip_view() = default;
+
+ constexpr explicit
+ zip_view(_Vs... views)
+ : _M_views(std::move(views)...)
+ { }
+
+ constexpr auto
+ begin() requires (!(__detail::__simple_view<_Vs> && ...))
+ { return iterator<false>(__detail::__tuple_transform(ranges::begin, _M_views)); }
+
+ constexpr auto
+ begin() const requires (range<const _Vs> && ...)
+ { return iterator<true>(__detail::__tuple_transform(ranges::begin, _M_views)); }
+
+ constexpr auto
+ end() requires (!(__detail::__simple_view<_Vs> && ...))
+ {
+ if constexpr (!__detail::__zip_is_common<_Vs...>)
+ return sentinel<false>(__detail::__tuple_transform(ranges::end, _M_views));
+ else if constexpr ((random_access_range<_Vs> && ...))
+ return begin() + iter_difference_t<iterator<false>>(size());
+ else
+ return iterator<false>(__detail::__tuple_transform(ranges::end, _M_views));
+ }
+
+ constexpr auto
+ end() const requires (range<const _Vs> && ...)
+ {
+ if constexpr (!__detail::__zip_is_common<const _Vs...>)
+ return sentinel<true>(__detail::__tuple_transform(ranges::end, _M_views));
+ else if constexpr ((random_access_range<const _Vs> && ...))
+ return begin() + iter_difference_t<iterator<true>>(size());
+ else
+ return iterator<true>(__detail::__tuple_transform(ranges::end, _M_views));
+ }
+
+ constexpr auto
+ size() requires (sized_range<_Vs> && ...)
+ {
+ return std::apply([](auto... sizes) {
+ using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(sizes)...>>;
+ return ranges::min({_CT(sizes)...});
+ }, __detail::__tuple_transform(ranges::size, _M_views));
+ }
+
+ constexpr auto
+ size() const requires (sized_range<const _Vs> && ...)
+ {
+ return std::apply([](auto... sizes) {
+ using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(sizes)...>>;
+ return ranges::min({_CT(sizes)...});
+ }, __detail::__tuple_transform(ranges::size, _M_views));
+ }
+ };
+
+ template<typename... _Rs>
+ zip_view(_Rs&&...) -> zip_view<views::all_t<_Rs>...>;
+
+ template<typename... _Views>
+ inline constexpr bool enable_borrowed_range<zip_view<_Views...>>
+ = (enable_borrowed_range<_Views> && ...);
+
+ namespace __detail
+ {
+ template<bool _Const, typename... _Vs>
+ concept __all_random_access
+ = (random_access_range<__maybe_const_t<_Const, _Vs>> && ...);
+
+ template<bool _Const, typename... _Vs>
+ concept __all_bidirectional
+ = (bidirectional_range<__maybe_const_t<_Const, _Vs>> && ...);
+
+ template<bool _Const, typename... _Vs>
+ concept __all_forward
+ = (forward_range<__maybe_const_t<_Const, _Vs>> && ...);
+
+ template<bool _Const, typename... _Views>
+ struct __zip_view_iter_cat
+ { };
+
+ template<bool _Const, typename... _Views>
+ requires __all_forward<_Const, _Views...>
+ struct __zip_view_iter_cat<_Const, _Views...>
+ { using iterator_category = input_iterator_tag; };
+ } // namespace __detail
+
+ template<input_range... _Vs>
+ requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0)
+ template<bool _Const>
+ class zip_view<_Vs...>::iterator
+ : public __detail::__zip_view_iter_cat<_Const, _Vs...>
+ {
+ __detail::__tuple_or_pair_t<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_current;
+
+ constexpr explicit
+ iterator(decltype(_M_current) __current)
+ : _M_current(std::move(__current))
+ { }
+
+ static auto
+ _S_iter_concept()
+ {
+ if constexpr (__detail::__all_random_access<_Const, _Vs...>)
+ return random_access_iterator_tag{};
+ else if constexpr (__detail::__all_bidirectional<_Const, _Vs...>)
+ return bidirectional_iterator_tag{};
+ else if constexpr (__detail::__all_forward<_Const, _Vs...>)
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }
+
+ public:
+ // iterator_category defined in __zip_view_iter_cat
+ using iterator_concept = decltype(_S_iter_concept());
+ using value_type
+ = __detail::__tuple_or_pair_t<range_value_t<__detail::__maybe_const_t<_Const, _Vs>>...>;
+ using difference_type
+ = common_type_t<range_difference_t<__detail::__maybe_const_t<_Const, _Vs>>...>;
+
+ iterator() = default;
+
+ constexpr
+ iterator(iterator<!_Const> __i)
+ requires _Const
+ && (convertible_to<iterator_t<_Vs>,
+ iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ : _M_current(std::move(__i._M_current))
+ { }
+
+ constexpr auto
+ operator*() const
+ {
+ auto __f = [](auto& __i) -> decltype(auto) {
+ return *__i;
+ };
+ return __detail::__tuple_transform(__f, _M_current);
+ }
+
+ constexpr iterator&
+ operator++()
+ {
+ __detail::__tuple_for_each([](auto& __i) { ++__i; }, _M_current);
+ return *this;
+ }
+
+ constexpr void
+ operator++(int)
+ { ++*this; }
+
+ constexpr iterator
+ operator++(int)
+ requires __detail::__all_forward<_Const, _Vs...>
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr iterator&
+ operator--()
+ requires __detail::__all_bidirectional<_Const, _Vs...>
+ {
+ __detail::__tuple_for_each([](auto& __i) { --__i; }, _M_current);
+ return *this;
+ }
+
+ constexpr iterator
+ operator--(int)
+ requires __detail::__all_bidirectional<_Const, _Vs...>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ constexpr iterator&
+ operator+=(difference_type __x)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __f = [&]<typename _It>(_It& __i) {
+ __i += iter_difference_t<_It>(__x);
+ };
+ __detail::__tuple_for_each(__f, _M_current);
+ return *this;
+ }
+
+ constexpr iterator&
+ operator-=(difference_type __x)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __f = [&]<typename _It>(_It& __i) {
+ __i -= iter_difference_t<_It>(__x);
+ };
+ __detail::__tuple_for_each(__f, _M_current);
+ return *this;
+ }
+
+ constexpr auto
+ operator[](difference_type __n) const
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __f = [&]<typename _It>(_It& __i) -> decltype(auto) {
+ return __i[iter_difference_t<_It>(__n)];
+ };
+ return __detail::__tuple_transform(__f, _M_current);
+ }
+
+ friend constexpr bool
+ operator==(const iterator& __x, const iterator& __y)
+ requires (equality_comparable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ {
+ if constexpr (__detail::__all_bidirectional<_Const, _Vs...>)
+ return __x._M_current == __y._M_current;
+ else
+ return [&]<size_t... _Is>(index_sequence<_Is...>) {
+ return ((std::get<_Is>(__x._M_current) == std::get<_Is>(__y._M_current)) || ...);
+ }(make_index_sequence<sizeof...(_Vs)>{});
+ }
+
+ friend constexpr bool
+ operator<(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __x._M_current < __y._M_current; }
+
+ friend constexpr bool
+ operator>(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __y < __x; }
+
+ friend constexpr bool
+ operator<=(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return !(__y < __x); }
+
+ friend constexpr bool
+ operator>=(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return !(__x < __y); }
+
+ friend constexpr auto
+ operator<=>(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ && (three_way_comparable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ { return __x._M_current <=> __y._M_current; }
+
+ friend constexpr iterator
+ operator+(const iterator& __i, difference_type __n)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __r = __i;
+ __r += __n;
+ return __r;
+ }
+
+ friend constexpr iterator
+ operator+(difference_type __n, const iterator& __i)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __r = __i;
+ __r += __n;
+ return __r;
+ }
+
+ friend constexpr iterator
+ operator-(const iterator& __i, difference_type __n)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ {
+ auto __r = __i;
+ __r -= __n;
+ return __r;
+ }
+
+ friend constexpr difference_type
+ operator-(const iterator& __x, const iterator& __y)
+ requires (sized_sentinel_for<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>,
+ iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ {
+ return [&]<size_t... _Is>(index_sequence<_Is...>) {
+ return ranges::min({difference_type(std::get<_Is>(__x._M_current)
+ - std::get<_Is>(__y._M_current))...},
+ ranges::less{},
+ [](difference_type __i) -> make_unsigned_t<difference_type> {
+ // TODO: use constexpr std::abs from P0533R9 once implemented
+ return __i < 0 ? -__i : __i;
+ });
+ }(make_index_sequence<sizeof...(_Vs)>{});
+ }
+
+ friend constexpr auto
+ iter_move(const iterator& __i)
+ { return __detail::__tuple_transform(ranges::iter_move, __i._M_current); }
+
+ friend constexpr void
+ iter_swap(const iterator& __l, const iterator& __r)
+ requires (indirectly_swappable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ {
+ [&]<size_t... _Is>(index_sequence<_Is...>) {
+ (ranges::iter_swap(std::get<_Is>(__l._M_current), std::get<_Is>(__r._M_current)), ...);
+ }(make_index_sequence<sizeof...(_Vs)>{});
+ }
+
+ friend class zip_view;
+ };
+
+ template<input_range... _Vs>
+ requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0)
+ template<bool _Const>
+ class zip_view<_Vs...>::sentinel
+ {
+ __detail::__tuple_or_pair_t<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_end;
+
+ constexpr explicit
+ sentinel(decltype(_M_end) __end)
+ : _M_end(__end)
+ { }
+
+ friend class zip_view;
+
+ public:
+ sentinel() = default;
+
+ constexpr
+ sentinel(sentinel<!_Const> __i)
+ requires _Const
+ && (convertible_to<sentinel_t<_Vs>,
+ sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...)
+ : _M_end(std::move(__i._M_end))
+ { }
+
+ template<bool _OtherConst>
+ requires (sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>,
+ iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...)
+ friend constexpr bool
+ operator==(const iterator<_OtherConst>& __x, const sentinel& __y)
+ {
+ return [&]<size_t... _Is>(index_sequence<_Is...>) {
+ return ((std::get<_Is>(__x._M_current) == std::get<_Is>(__y._M_end)) || ...);
+ }(make_index_sequence<sizeof...(_Vs)>{});
+ }
+
+ template<bool _OtherConst>
+ requires (sized_sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>,
+ iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...)
+ friend constexpr auto
+ operator-(const iterator<_OtherConst>& __x, const sentinel& __y)
+ {
+ using _Ret
+ = common_type_t<range_difference_t<__detail::__maybe_const_t<_OtherConst, _Vs>>...>;
+ return [&]<size_t... _Is>(index_sequence<_Is...>) {
+ return ranges::min({_Ret(std::get<_Is>(__x._M_current) - std::get<_Is>(__y._M_end))...},
+ ranges::less{},
+ [](_Ret __i) -> make_unsigned_t<_Ret> {
+ // TODO: use constexpr std::abs from P0533R9 once implemented
+ return __i < 0 ? -__i : __i;
+ });
+ }(make_index_sequence<sizeof...(_Vs)>{});
+ }
+
+ template<bool _OtherConst>
+ requires (sized_sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>,
+ iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...)
+ friend constexpr auto
+ operator-(const sentinel& __y, const iterator<_OtherConst>& __x)
+ { return -(__x - __y); }
+ };
+
+ namespace views
+ {
+ namespace __detail
+ {
+ template<typename... _Ts>
+ concept __can_zip_view
+ = requires { zip_view<all_t<_Ts>...>(std::declval<_Ts>()...); };
+ }
+
+ struct _Zip
+ {
+ template<typename... _Ts>
+ requires (sizeof...(_Ts) == 0 || __detail::__can_zip_view<_Ts...>)
+ [[nodiscard]]
+ constexpr auto
+ operator()(_Ts&&... __ts) const
+ {
+ if constexpr (sizeof...(_Ts) == 0)
+ return views::empty<tuple<>>;
+ else
+ return zip_view<all_t<_Ts>...>(std::forward<_Ts>(__ts)...);
+ }
+ };
+
+ inline constexpr _Zip zip;
+ }
+#endif // C++23
} // namespace ranges
namespace views = ranges::views;
diff --git a/libstdc++-v3/testsuite/std/ranges/zip/1.cc b/libstdc++-v3/testsuite/std/ranges/zip/1.cc
new file mode 100644
index 00000000000..4d5835829dd
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/zip/1.cc
@@ -0,0 +1,101 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <ranges>
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::views;
+
+constexpr bool
+test01()
+{
+ static_assert(ranges::empty(views::zip()));
+ static_assert(ranges::empty(views::empty<int>));
+
+ auto z1 = views::zip(std::array{1, 2});
+ const auto i0 = z1.begin(), i1 = z1.begin() + 1;
+ VERIFY( i0 + 1 - 1 == i0 );
+ VERIFY( i0 < i1 );
+ VERIFY( i1 < z1.end() );
+ VERIFY( i1 - i0 == 1 );
+ VERIFY( i0 - i1 == -1 );
+ VERIFY( z1.end() - i1 == 1 );
+ VERIFY( i1 - z1.end() == -1 );
+ ranges::iter_swap(i0, i1);
+ VERIFY( ranges::equal(std::move(z1) | views::keys, (int[]){2, 1}) );
+
+ auto z2 = views::zip(std::array{1, 2}, std::array{3, 4, 5});
+ VERIFY( ranges::size(z2) == 2 );
+ VERIFY( ranges::size(std::as_const(z2)) == 2 );
+ VERIFY( z2[0].first == 1 && z2[0].second == 3 );
+ VERIFY( z2[1].first == 2 && z2[1].second == 4 );
+ for (const auto [x, y] : z2)
+ {
+ VERIFY( y - x == 2 );
+ std::swap(x, y);
+ }
+
+ int x[2] = {1, 2}, y[2] = {3, 4}, z[2] = {5, 6};
+ const auto z3 = views::zip(x, y, z);
+ VERIFY( ranges::size(z3) == 2 );
+ for (int i = 0; i < ranges::size(x); i++)
+ {
+ VERIFY( &std::get<0>(z3[i]) == &x[i] );
+ VERIFY( &std::get<1>(z3[i]) == &y[i] );
+ VERIFY( &std::get<2>(z3[i]) == &z[i] );
+ }
+
+ return true;
+}
+
+constexpr bool
+test02()
+{
+ using __gnu_test::test_input_range;
+ using __gnu_test::test_forward_range;
+ using __gnu_test::test_random_access_range;
+
+ using ty1 = ranges::zip_view<views::all_t<test_forward_range<int>>,
+ views::all_t<test_random_access_range<int>>>;
+ static_assert(ranges::forward_range<ty1>);
+ static_assert(!ranges::random_access_range<ty1>);
+ static_assert(!ranges::sized_range<ty1>);
+
+ using ty2 = ranges::zip_view<views::all_t<test_forward_range<int>>,
+ views::all_t<test_input_range<int>>,
+ views::all_t<test_forward_range<int>>>;
+ static_assert(ranges::input_range<ty2>);
+ static_assert(!ranges::forward_range<ty2>);
+ static_assert(!ranges::sized_range<ty2>);
+
+ return true;
+}
+
+constexpr bool
+test03()
+{
+ int u[] = {1, 2, 3, 4}, v[] = {4, 5, 6}, w[] = {7, 8, 9, 10};
+ auto z = views::zip(u | views::filter([](auto) { return true; }), v, w);
+ using ty = decltype(z);
+ static_assert(ranges::forward_range<ty>);
+ static_assert(!ranges::common_range<ty>);
+ static_assert(!ranges::sized_range<ty>);
+ VERIFY( z.begin() == z.begin() );
+ VERIFY( z.begin() != z.end() );
+ VERIFY( ranges::next(z.begin(), 3) == z.end() );
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(test02());
+ static_assert(test03());
+}
--
2.37.2.382.g795ea8776b
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple
2022-08-23 1:34 [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Patrick Palka
2022-08-23 1:34 ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Patrick Palka
2022-08-23 1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
@ 2022-08-23 9:15 ` Jonathan Wakely
2022-08-23 13:44 ` Patrick Palka
2 siblings, 1 reply; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-23 9:15 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Tue, 23 Aug 2022 at 02:35, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> P2321R2 adds new conditionally explicit constructors to std::tuple which
> we'll concisely implement in a subsequent patch using explicit(bool), like
> in our C++20 std::pair implementation. But before we can do that, this
> patch first adds members to _TupleConstraints that test for constructibility
> and convertibility separately; we'll use the first in the new constructors'
> constraints, and the second in their explicit specifier.
>
> In passing, this patch also redefines the existing predicates
> __is_ex/implicitly_constructible in terms of these new members. This
> seems to reduce compile time and memory usage by about 10% for large
Nice.
> tuples when using the relevant constructors constrained by
> _Explicit/_ImplicitCtor (since we no longer have to redundantly expand
> and process is_constructible<_Types, _UTypes>... twice for each pair of
> such constructors). In order to retain maximal short circuiting, do
> this only when constexpr if is available.
Would we get similar benefits for C++11 and C++14 by doing:
return __and_<__and_<is_constructible<_Types, _UTypes>...>,
__and_<is_convertible<_UTypes, _Types>...>
>::value;
This is slightly more work in total, but if we have __and_<A,B> and
__and_<A,__not_<B>> then the A and B instantiations will be cached and
can be reused.
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
Yes, thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2
2022-08-23 1:34 ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Patrick Palka
@ 2022-08-23 12:03 ` Jonathan Wakely
2022-08-23 15:14 ` Patrick Palka
0 siblings, 1 reply; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-23 12:03 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Tue, 23 Aug 2022 at 02:36, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -212,6 +212,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> swap(second, __p.second);
> }
>
> +#if __cplusplus > 202002L
> + /// Swap the first members and then the second members.
> + constexpr void
> + swap(const pair& __p) const
> + noexcept(__and_<__is_nothrow_swappable<const _T1>,
> + __is_nothrow_swappable<const _T2>>::value)
This could use __and_v (which is just __and_::value today, but could
theoretically be optimized to use a requires expression and avoid
instantiating __and_ one day).
Is consistency with the C++11 overload more important? I *hope* we
won't need to make many changes to these noexcept-specifiers, so the
maintenance burden of using __ad_::value in one and __and_v in the
other shouldn't be too high.
> @@ -710,6 +792,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> noexcept(noexcept(__x.swap(__y)))
> { __x.swap(__y); }
>
> +#if __cplusplus > 202002L
> + template<typename _T1, typename _T2>
> + requires is_swappable<const _T1>::value && is_swappable<const _T2>::value
is_swappable_v instead of ::value here ... this is already using a
requires-clause and so is substantially different to the old overload
anyway.
> +
> // tuple swap
> _GLIBCXX20_CONSTEXPR
> void
> swap(tuple& __in)
> noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
> { _Inherited::_M_swap(__in); }
> +
> +#if __cplusplus > 202002L
> + constexpr void
> + swap(const tuple& __in) const
> + noexcept(__and_<__is_nothrow_swappable<const _Elements>...>::value)
__and_v ?
> _GLIBCXX20_CONSTEXPR
> void
> swap(tuple& __in)
> noexcept(__and_<__is_nothrow_swappable<_T1>,
> __is_nothrow_swappable<_T2>>::value)
> { _Inherited::_M_swap(__in); }
> +
> +#if __cplusplus > 202002L
> + constexpr void
> + swap(const tuple& __in) const
> + noexcept(__and_<__is_nothrow_swappable<const _T1>,
> + __is_nothrow_swappable<const _T2>>::value)
__and_v ?
Thanks for doing this, those changes looked tedious to implement and test!
If you agree with the suggestions to use _v variable templates, this
is OK for trunk with those changes. I am willing to be persuaded to
not use the variable templates if there's a good reason I've missed.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple
2022-08-23 9:15 ` [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Jonathan Wakely
@ 2022-08-23 13:44 ` Patrick Palka
2022-08-23 14:53 ` Jonathan Wakely
0 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2022-08-23 13:44 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++
On Tue, 23 Aug 2022, Jonathan Wakely wrote:
> On Tue, 23 Aug 2022 at 02:35, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> >
> > P2321R2 adds new conditionally explicit constructors to std::tuple which
> > we'll concisely implement in a subsequent patch using explicit(bool), like
> > in our C++20 std::pair implementation. But before we can do that, this
> > patch first adds members to _TupleConstraints that test for constructibility
> > and convertibility separately; we'll use the first in the new constructors'
> > constraints, and the second in their explicit specifier.
> >
> > In passing, this patch also redefines the existing predicates
> > __is_ex/implicitly_constructible in terms of these new members. This
> > seems to reduce compile time and memory usage by about 10% for large
>
> Nice.
>
> > tuples when using the relevant constructors constrained by
> > _Explicit/_ImplicitCtor (since we no longer have to redundantly expand
> > and process is_constructible<_Types, _UTypes>... twice for each pair of
> > such constructors). In order to retain maximal short circuiting, do
> > this only when constexpr if is available.
>
> Would we get similar benefits for C++11 and C++14 by doing:
>
> return __and_<__and_<is_constructible<_Types, _UTypes>...>,
> __and_<is_convertible<_UTypes, _Types>...>
> >::value;
>
> This is slightly more work in total, but if we have __and_<A,B> and
> __and_<A,__not_<B>> then the A and B instantiations will be cached and
> can be reused.
Good idea, it seems we get pretty much the same 10% improvement for
C++11/14 with this approach. I reckon we might as well then define
__convertible and __constructible as alias templates instead of as
variable templates and use them unconditionally in
__is_im/explicitly_constructible for benefit of all language versions.
Using constexpr if instead of the outer __and_ seems to yield a marginal
improvement at best and __and_v is currently just __and_::value, so it
doesn't seem worth it to have different definitions for C++17 at least
for now.
What do you think about the following?
-- >8 --
libstdc++-v3/ChangeLog:
* include/std/tuple (_TupleConstraints::__convertible): Define.
(_TupleConstraints::__constructible): Likewise.
(_TupleConstraints::__is_explicitly_constructible): Redefine this
in terms of __convertible and __constructible.
(_TupleConstraints::__is_implicitly_constructible): Likewise.
---
libstdc++-v3/include/std/tuple | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 6d0060a191c..f8f48ccc370 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -553,15 +553,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<bool, typename... _Types>
struct _TupleConstraints
{
+ template<typename... _UTypes>
+ using __constructible = __and_<is_constructible<_Types, _UTypes>...>;
+
+ template<typename... _UTypes>
+ using __convertible = __and_<is_convertible<_UTypes, _Types>...>;
+
// Constraint for a non-explicit constructor.
// True iff each Ti in _Types... can be constructed from Ui in _UTypes...
// and every Ui is implicitly convertible to Ti.
template<typename... _UTypes>
static constexpr bool __is_implicitly_constructible()
{
- return __and_<is_constructible<_Types, _UTypes>...,
- is_convertible<_UTypes, _Types>...
- >::value;
+ return __and_<__constructible<_UTypes...>,
+ __convertible<_UTypes...>>::value;
}
// Constraint for a non-explicit constructor.
@@ -570,9 +575,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _UTypes>
static constexpr bool __is_explicitly_constructible()
{
- return __and_<is_constructible<_Types, _UTypes>...,
- __not_<__and_<is_convertible<_UTypes, _Types>...>>
- >::value;
+ return __and_<__constructible<_UTypes...>,
+ __not_<__convertible<_UTypes...>>>::value;
}
static constexpr bool __is_implicitly_default_constructible()
--
2.37.2.382.g795ea8776b
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple
2022-08-23 13:44 ` Patrick Palka
@ 2022-08-23 14:53 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-23 14:53 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Tue, 23 Aug 2022 at 14:44, Patrick Palka <ppalka@redhat.com> wrote:
>
> On Tue, 23 Aug 2022, Jonathan Wakely wrote:
>
> > On Tue, 23 Aug 2022 at 02:35, Patrick Palka via Libstdc++
> > <libstdc++@gcc.gnu.org> wrote:
> > >
> > > P2321R2 adds new conditionally explicit constructors to std::tuple which
> > > we'll concisely implement in a subsequent patch using explicit(bool), like
> > > in our C++20 std::pair implementation. But before we can do that, this
> > > patch first adds members to _TupleConstraints that test for constructibility
> > > and convertibility separately; we'll use the first in the new constructors'
> > > constraints, and the second in their explicit specifier.
> > >
> > > In passing, this patch also redefines the existing predicates
> > > __is_ex/implicitly_constructible in terms of these new members. This
> > > seems to reduce compile time and memory usage by about 10% for large
> >
> > Nice.
> >
> > > tuples when using the relevant constructors constrained by
> > > _Explicit/_ImplicitCtor (since we no longer have to redundantly expand
> > > and process is_constructible<_Types, _UTypes>... twice for each pair of
> > > such constructors). In order to retain maximal short circuiting, do
> > > this only when constexpr if is available.
> >
> > Would we get similar benefits for C++11 and C++14 by doing:
> >
> > return __and_<__and_<is_constructible<_Types, _UTypes>...>,
> > __and_<is_convertible<_UTypes, _Types>...>
> > >::value;
> >
> > This is slightly more work in total, but if we have __and_<A,B> and
> > __and_<A,__not_<B>> then the A and B instantiations will be cached and
> > can be reused.
>
> Good idea, it seems we get pretty much the same 10% improvement for
> C++11/14 with this approach. I reckon we might as well then define
> __convertible and __constructible as alias templates instead of as
> variable templates and use them unconditionally in
> __is_im/explicitly_constructible for benefit of all language versions.
I had a similar thought after hitting send.
> Using constexpr if instead of the outer __and_ seems to yield a marginal
> improvement at best and __and_v is currently just __and_::value, so it
> doesn't seem worth it to have different definitions for C++17 at least
> for now.
>
> What do you think about the following?
OK for trunk - thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2
2022-08-23 12:03 ` Jonathan Wakely
@ 2022-08-23 15:14 ` Patrick Palka
0 siblings, 0 replies; 11+ messages in thread
From: Patrick Palka @ 2022-08-23 15:14 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++
On Tue, 23 Aug 2022, Jonathan Wakely wrote:
> On Tue, 23 Aug 2022 at 02:36, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> > --- a/libstdc++-v3/include/bits/stl_pair.h
> > +++ b/libstdc++-v3/include/bits/stl_pair.h
> > @@ -212,6 +212,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > swap(second, __p.second);
> > }
> >
> > +#if __cplusplus > 202002L
> > + /// Swap the first members and then the second members.
> > + constexpr void
> > + swap(const pair& __p) const
> > + noexcept(__and_<__is_nothrow_swappable<const _T1>,
> > + __is_nothrow_swappable<const _T2>>::value)
>
> This could use __and_v (which is just __and_::value today, but could
> theoretically be optimized to use a requires expression and avoid
> instantiating __and_ one day).
>
> Is consistency with the C++11 overload more important? I *hope* we
> won't need to make many changes to these noexcept-specifiers, so the
> maintenance burden of using __ad_::value in one and __and_v in the
> other shouldn't be too high.
Makes sense.
>
> > @@ -710,6 +792,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > noexcept(noexcept(__x.swap(__y)))
> > { __x.swap(__y); }
> >
> > +#if __cplusplus > 202002L
> > + template<typename _T1, typename _T2>
> > + requires is_swappable<const _T1>::value && is_swappable<const _T2>::value
>
> is_swappable_v instead of ::value here ... this is already using a
> requires-clause and so is substantially different to the old overload
> anyway.
>
>
>
> > +
> > // tuple swap
> > _GLIBCXX20_CONSTEXPR
> > void
> > swap(tuple& __in)
> > noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
> > { _Inherited::_M_swap(__in); }
> > +
> > +#if __cplusplus > 202002L
> > + constexpr void
> > + swap(const tuple& __in) const
> > + noexcept(__and_<__is_nothrow_swappable<const _Elements>...>::value)
>
> __and_v ?
>
>
>
>
> > _GLIBCXX20_CONSTEXPR
> > void
> > swap(tuple& __in)
> > noexcept(__and_<__is_nothrow_swappable<_T1>,
> > __is_nothrow_swappable<_T2>>::value)
> > { _Inherited::_M_swap(__in); }
> > +
> > +#if __cplusplus > 202002L
> > + constexpr void
> > + swap(const tuple& __in) const
> > + noexcept(__and_<__is_nothrow_swappable<const _T1>,
> > + __is_nothrow_swappable<const _T2>>::value)
>
> __and_v ?
>
>
> Thanks for doing this, those changes looked tedious to implement and test!
>
> If you agree with the suggestions to use _v variable templates, this
> is OK for trunk with those changes. I am willing to be persuaded to
> not use the variable templates if there's a good reason I've missed.
Agreed on all points! Thanks a lot.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] libstdc++: Implement ranges::zip_view from P2321R2
2022-08-23 1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
@ 2022-08-24 12:15 ` Jonathan Wakely
2022-08-26 20:05 ` Jonathan Wakely
1 sibling, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-24 12:15 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Tue, 23 Aug 2022 at 02:38, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested on 86_64-pc-linux-gnu, does this look OK for trunk?
As discussed privately, please remove the #include <cstdint> (which is
not needed, and adds declarations to the global namespace that we
don't need).
OK with that change, thanks.
N.B. re the TODOs, we already have a constexpr abs in <numeric> but
it's probably not worth including that just for one function, that
you've already managed without.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] libstdc++: Implement ranges::zip_view from P2321R2
2022-08-23 1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
2022-08-24 12:15 ` Jonathan Wakely
@ 2022-08-26 20:05 ` Jonathan Wakely
2022-08-31 10:12 ` Jonathan Wakely
1 sibling, 1 reply; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-26 20:05 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Tue, 23 Aug 2022 at 02:38, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
> + template<typename _Fp, typename _Tuple>
> + constexpr void
> + __tuple_for_each(_Fp&& __f, _Tuple&& __tuple)
> + {
> + std::apply([&]<typename... _Ts>(_Ts&&... __elts) {
> + (std::__invoke(__f, std::forward<_Ts>(__elts)), ...);
> + }, std::forward<_Tuple>(__tuple));
Nicole Mazzuca noticed a problem with the definition of
tuple-for-each, which I've just added to the issues list:
https://cplusplus.github.io/LWG/issue3755
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/3] libstdc++: Implement ranges::zip_view from P2321R2
2022-08-26 20:05 ` Jonathan Wakely
@ 2022-08-31 10:12 ` Jonathan Wakely
0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2022-08-31 10:12 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Fri, 26 Aug 2022 at 21:05, Jonathan Wakely wrote:
>
> On Tue, 23 Aug 2022 at 02:38, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> > + template<typename _Fp, typename _Tuple>
> > + constexpr void
> > + __tuple_for_each(_Fp&& __f, _Tuple&& __tuple)
> > + {
> > + std::apply([&]<typename... _Ts>(_Ts&&... __elts) {
> > + (std::__invoke(__f, std::forward<_Ts>(__elts)), ...);
> > + }, std::forward<_Tuple>(__tuple));
>
>
> Nicole Mazzuca noticed a problem with the definition of
> tuple-for-each, which I've just added to the issues list:
> https://cplusplus.github.io/LWG/issue3755
Ignore this. The __tuple_for_each function is only ever called by the
library, so there's no way for something like Evil to be passed to it.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-08-31 10:13 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-23 1:34 [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Patrick Palka
2022-08-23 1:34 ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Patrick Palka
2022-08-23 12:03 ` Jonathan Wakely
2022-08-23 15:14 ` Patrick Palka
2022-08-23 1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
2022-08-24 12:15 ` Jonathan Wakely
2022-08-26 20:05 ` Jonathan Wakely
2022-08-31 10:12 ` Jonathan Wakely
2022-08-23 9:15 ` [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Jonathan Wakely
2022-08-23 13:44 ` Patrick Palka
2022-08-23 14:53 ` Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).