From: Tomasz Kamiński <[email protected]>
This patch add's tuple protocol support for specialization of
span with fixed size. This change is breaking in case when
two overloads requiring conversion from argument are found, for
example the call foo(std::span<int, 5>) is now ambigous for
for forlowing overload:
void foo(std::span<int>);
void foo(std::pair<int, int>);
This is same as in case of adding tuple support for complex.
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (std::span)
(std::get<_Idx>(std::span<_Idx, _Extent>)): Forward declare.
(_is_tuple_like): Define specialization for span.
* include/bits/version.def: Bump tuple_like.
* include/bits/version.h: Regenerate.
* include/std/span (std::tuple_size, std::tuple_element):
Define specialization for span.
(std::get<_Idx>(std::span<_Idx, _Extent>)): Define.
* testsuite/23_containers/span/tuple.cc: New test.
---
Note targeting a merge, the paper is in flight, just reporting
implementation exprience.
Tested on x86_64-linux locally.
libstdc++-v3/include/bits/stl_pair.h | 16 ++
libstdc++-v3/include/bits/version.def | 2 +-
libstdc++-v3/include/bits/version.h | 4 +-
libstdc++-v3/include/std/span | 30 ++
.../testsuite/23_containers/span/tuple.cc | 269 ++++++++++++++++++
5 files changed, 318 insertions(+), 3 deletions(-)
create mode 100644 libstdc++-v3/testsuite/23_containers/span/tuple.cc
diff --git a/libstdc++-v3/include/bits/stl_pair.h
b/libstdc++-v3/include/bits/stl_pair.h
index 231d0bbd1f4..862b1930b6e 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -104,6 +104,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
class complex;
+#if __glibcxx_tuple_like >= 202510L
+ template<typename _Type, size_t _Extent>
+ class span;
+#endif
+
template<size_t _Int, class _Tp1, class _Tp2>
constexpr typename tuple_element<_Int, pair<_Tp1, _Tp2>>::type&
get(pair<_Tp1, _Tp2>& __in) noexcept;
@@ -167,6 +172,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
get(const complex<_Tp>&&) noexcept;
#endif
+#if __glibcxx_tuple_like >= 202510L
+ template<size_t _Idx, typename _Type, size_t _Extent>
+ constexpr _Type&
+ get(span<_Type, _Extent> __span) noexcept;
+#endif
+
#if ! __cpp_lib_concepts
// Concept utility functions, reused in conditionally-explicit
// constructors.
@@ -255,6 +266,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp, size_t _Nm>
inline constexpr bool __is_tuple_like_v<array<_Tp, _Nm>> = true;
+#if __glibcxx_tuple_like >= 202510L
+ template<typename _Tp, size_t _Nm>
+ inline constexpr bool __is_tuple_like_v<span<_Tp, _Nm>> = (_Nm !=
size_t(-1));
+#endif
+
// __is_tuple_like_v<subrange> is defined in <bits/ranges_util.h>.
template<typename _Tp>
diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 3a26234f87e..c31db13291d 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1909,7 +1909,7 @@ ftms = {
ftms = {
name = tuple_like;
values = {
- v = 202311;
+ v = 202510;
cxxmin = 26;
extra_cond = "__cpp_explicit_this_parameter >= 202110L";
};
diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index 46e4c1121e7..aab98115b66 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2135,9 +2135,9 @@
#if !defined(__cpp_lib_tuple_like)
# if (__cplusplus > 202302L) && (__cpp_explicit_this_parameter >= 202110L)
-# define __glibcxx_tuple_like 202311L
+# define __glibcxx_tuple_like 202510L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_tuple_like)
-# define __cpp_lib_tuple_like 202311L
+# define __cpp_lib_tuple_like 202510L
# endif
# elif (__cplusplus >= 202100L)
# define __glibcxx_tuple_like 202207L
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 58089113565..abead33252d 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -40,6 +40,7 @@
#define __glibcxx_want_span
#define __glibcxx_want_span_initializer_list
+#define __glibcxx_want_span_tuple_like
#include <bits/version.h>
#ifdef __cpp_lib_span // C++ >= 20 && concepts
@@ -50,6 +51,10 @@
#ifdef __cpp_lib_span_initializer_list
# include <initializer_list>
#endif
+#if __glibcxx_tuple_like >= 202510L
+# include <bits/utility.h> // tuple_size, tuple_element
+#endif
+
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -545,6 +550,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return span<byte, extent>{data, size};
}
+#if __glibcxx_tuple_like >= 202510L
+ template<typename _Type, size_t _Extent>
+ requires (_Extent != dynamic_extent)
+ struct tuple_size<span<_Type, _Extent>>
+ : public integral_constant<size_t, _Extent> { };
+
+ template<size_t _Idx, typename _Type, size_t _Extent>
+ struct tuple_element<_Idx, span<_Type, _Extent>>
+ {
+ static_assert(_Extent != dynamic_extent);
+ static_assert(_Idx < _Extent);
+
+ using type = _Type&;
+ };
+
+ template<size_t _Idx, typename _Type, size_t _Extent>
+ constexpr _Type&
+ get(span<_Type, _Extent> __span) noexcept
+ {
+ static_assert(_Extent != dynamic_extent);
+ static_assert(_Idx < _Extent);
+ return __span[_Idx];
+ }
+#endif
+
namespace ranges
{
// Opt-in to borrowed_range concept
diff --git a/libstdc++-v3/testsuite/23_containers/span/tuple.cc
b/libstdc++-v3/testsuite/23_containers/span/tuple.cc
new file mode 100644
index 00000000000..64a14bf8391
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/span/tuple.cc
@@ -0,0 +1,269 @@
+// { dg-do run { target c++26 } }
+
+#include <span>
+
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <testsuite_hooks.h>
+#include <utility>
+#include <vector>
+
+template<typename T>
+concept hasTupleSize = requires
+{
+ std::tuple_size<T>::value;
+};
+
+void
+testTupleProtocol()
+{
+ int arr[]{0, 1, 2, 3, 4};
+ std::span<int, 5> s(arr);
+ std::span<const int, 5> sc = s;
+ const std::span<int, 5> cs = s;
+
+ static_assert( !hasTupleSize<std::span<int>> );
+ static_assert( !hasTupleSize<std::span<const int>> );
+
+ static_assert( std::tuple_size_v<std::span<int, 2>> == 2 );
+ static_assert( std::tuple_size_v<const std::span<int, 3>> == 3 );
+ static_assert( std::tuple_size_v<std::span<const int, 4>> == 4 );
+
+ VERIFY( std::get<0>(s) == 0 );
+ VERIFY( &std::get<4>(s) == arr+4 );
+ VERIFY( std::is_same_v<decltype(std::get<0>(s)), int&> );
+
+ VERIFY( std::get<1>(sc) == 1 );
+ VERIFY( &std::get<3>(sc) == arr+3 );
+ VERIFY( std::is_same_v<decltype(std::get<1>(sc)), const int&> );
+
+ VERIFY( std::get<2>(cs) == 2 );
+ VERIFY( &std::get<2>(cs) == arr+2 );
+ VERIFY( std::is_same_v<decltype(std::get<2>(cs)), int&> );
+
+ auto [e0, e1, e2, e3, e4] = s;
+ VERIFY( &e1 == arr+1 );
+ VERIFY( std::is_same_v<decltype(e1), int&> );
+
+ std::apply([&arr]<typename... Ts>(Ts&&...)
+ {
+ static_assert( (std::is_same_v<Ts&&, int&> && ...) );
+ }, s);
+
+ std::apply([&arr]<typename... Ts>(Ts&&...)
+ {
+ static_assert( (std::is_same_v<Ts&&, const int&> && ...) );
+ }, sc);
+}
+
+void
+testPairInterop()
+{
+ int arr[]{0, 1};
+ std::span<int, 2> s(arr, 2);
+
+ static_assert( !std::is_constructible_v<std::pair<int, int>,
+ std::span<int>>);
+ static_assert( !std::is_constructible_v<std::pair<int, int>,
+ std::span<int, 1>>);
+ static_assert( !std::is_constructible_v<std::pair<int, int>,
+ std::span<int, 3>>);
+
+ std::pair<int, int> pi(s);
+ VERIFY( pi.first == 0 );
+ VERIFY( pi.second == 1 );
+
+ std::pair<long, long> pl(s);
+ VERIFY( pl.first == 0 );
+ VERIFY( pl.second == 1 );
+
+ std::pair<int&, int&> pr(s);
+ VERIFY( &pr.first == s.data()+0 );
+ VERIFY( &pr.second == s.data()+1 );
+
+ static_assert( !std::is_constructible_v<std::pair<int&, int&>,
+ std::span<int const, 2>>);
+}
+
+void
+testTupleInterop()
+{
+ int arr[]{0, 1, 2};
+ std::span<int, 3> s(arr, 2);
+
+ static_assert( !std::is_constructible_v<std::tuple<int, int, int>,
+ std::span<int>>);
+ static_assert( !std::is_constructible_v<std::tuple<int, int, int>,
+ std::span<int, 1>>);
+ static_assert( !std::is_constructible_v<std::tuple<int, int, int>,
+ std::span<int, 4>>);
+
+ std::tuple<int, int, int> ti(s);
+ VERIFY( std::get<0>(ti) == 0 );
+ VERIFY( std::get<1>(ti) == 1 );
+ VERIFY( std::get<2>(ti) == 2 );
+
+ std::tuple<long, long, long> tl(s);
+ VERIFY( std::get<0>(tl) == 0 );
+ VERIFY( std::get<1>(tl) == 1 );
+ VERIFY( std::get<2>(tl) == 2 );
+
+ std::tuple<int&, int&, int&> tr(s);
+ VERIFY( &std::get<0>(tr) == s.data()+0 );
+ VERIFY( &std::get<1>(tr) == s.data()+1 );
+ VERIFY( &std::get<2>(tr) == s.data()+2 );
+
+ static_assert( !std::is_constructible_v<std::tuple<int&, int&, int&>,
+ std::span<const int, 3>> );
+}
+
+void testTupleCat()
+{
+ int arr[]{1, 2, 3, 4, 5};
+ std::span<int, 2> s1(arr+0, 2);
+ std::span<const int, 3> s2(arr+2, 3);
+
+ auto cs = std::tuple_cat(s1, s2);
+ static_assert( std::is_same_v<
+ decltype(cs),
+ std::tuple<int&, int&, const int&, const int&, const int&>> );
+ VERIFY( &std::get<0>(cs) == arr+0 );
+ VERIFY( &std::get<4>(cs) == arr+4 );
+
+ std::pair<int, int> p(11, 12);
+ auto cp = std::tuple_cat(p, s2);
+ static_assert( std::is_same_v<
+ decltype(cp),
+ std::tuple<int, int, const int&, const int&, const int&>> );
+ VERIFY( std::get<1>(cp) == 12 );
+ VERIFY( &std::get<3>(cp) == arr+3 );
+
+ std::tuple<int, int, int> t(23, 24, 25);
+ auto ct = std::tuple_cat(s1, t);
+ static_assert( std::is_same_v<
+ decltype(ct),
+ std::tuple<int&, int&, int, int, int>> );
+ VERIFY( &std::get<1>(ct) == arr+1 );
+ VERIFY( std::get<2>(ct) == 23 );
+}
+
+void testCommonType()
+{
+ static_assert( std::is_same_v<
+ std::common_type_t<std::span<int, 2>, std::tuple<int, int>>,
+ std::tuple<int, int>> );
+ static_assert( std::is_same_v<
+ std::common_type_t<std::span<const int, 2>, std::tuple<int&, const int>>,
+ std::tuple<int, int>> );
+
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int, int>>,
+ std::tuple<int, int>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int, int>&>,
+ std::tuple<int&, int&>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<const int, 2>, std::tuple<int, int>&>,
+ std::tuple<const int&, const int&>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int, int> const&>,
+ std::tuple<const int&, const int&>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int, int>&&>,
+ std::tuple<const int&, const int&>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<const int, 2>, std::tuple<int, int>&&>,
+ std::tuple<const int&, const int&>> );
+
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int&, int>>,
+ std::tuple<int&, int>> );
+ static_assert( std::is_same_v<
+ std::common_reference_t<std::span<int, 2>, std::tuple<int&, int> const&>,
+ std::tuple<int&, const int&>> );
+}
+
+void testViews()
+{
+ int a2d[5][2]{{10, 20}, {11, 21}, {12, 22}, {13, 23}, {14, 24}};
+ std::vector<std::span<int, 2>> vs;
+ for (auto& ar : a2d)
+ vs.emplace_back(ar);
+
+ for (int& x : vs | std::views::elements<0>)
+ x += 50;
+
+ VERIFY( a2d[0][0] == 60 );
+ VERIFY( a2d[0][1] == 20 );
+ VERIFY( a2d[4][0] == 64 );
+ VERIFY( a2d[4][1] == 24 );
+
+ for (int& x : vs | std::views::elements<1>)
+ x += 30;
+
+ VERIFY( a2d[1][0] == 61 );
+ VERIFY( a2d[1][1] == 51 );
+ VERIFY( a2d[3][0] == 63 );
+ VERIFY( a2d[3][1] == 53 );
+}
+
+struct SpanOnly
+{
+ void operator()(std::span<int>);
+};
+static_assert( std::is_invocable_v<SpanOnly, std::span<int, 2>> );
+
+struct SpanAndPair
+{
+ void operator()(std::span<int>);
+ void operator()(std::pair<int, int>);
+};
+// Ambigous, due two user defined conversions
+static_assert( !std::is_invocable_v<SpanAndPair, std::span<int, 2>> );
+
+struct SpanTemplated
+{
+ template<size_t N>
+ int operator()(std::span<const int, N>);
+
+ void operator()(std::pair<int, int>);
+};
+// Span overload is direct match
+static_assert( std::is_invocable_v<SpanTemplated, std::span<const int, 2>> );
+static_assert( std::is_same_v<
+ std::invoke_result_t<SpanTemplated, std::span<const int, 2>>,
+ int> );
+// Template parameter N cannot be deduced from span, only pair overload
+// is viable.
+static_assert( std::is_invocable_v<SpanTemplated, std::span<int, 2>> );
+static_assert( std::is_same_v<
+ std::invoke_result_t<SpanTemplated, std::span<int, 2>>,
+ void> );
+
+struct PairTemplated
+{
+ int operator()(std::span<const int>);
+
+ template<typename T>
+ void operator()(std::pair<T, T>);
+};
+// Span overload is only candidate as pair arguments cannot be deduced
+static_assert( std::is_invocable_v<PairTemplated, std::span<const int, 2>> );
+static_assert( std::is_same_v<
+ std::invoke_result_t<PairTemplated, std::span<const int, 2>>,
+ int> );
+static_assert( std::is_invocable_v<PairTemplated, std::span<int, 2>> );
+static_assert( std::is_same_v<
+ std::invoke_result_t<PairTemplated, std::span<const int, 2>>,
+ int> );
+
+int main()
+{
+ testTupleProtocol();
+ testPairInterop();
+ testTupleInterop();
+ testTupleCat();
+ testCommonType();
+ testViews();
+}
--
2.51.0