[PATCH] libstdc++: Implement P0849R8 auto(x) library changes

Patrick Palka ppalka@redhat.com
Mon Oct 7 23:58:37 GMT 2024


Tested on x86_64-pc-linux-gnu, does this look OK for trunk only?
This doesn't seem worth backporting since there should be no
behavior change.

-- >8 --

This implements the library changes in P0849R8 "auto(x): decay-copy
in the language" which consist of replacing most uses of the
exposition-only function decay-copy with auto(x) throughout the library
wording.

Note the main difference between the two is that decay-copy materializes
its argument whereas auto(x) doesn't, and so the latter is a no-op when
its argument is a prvalue.  Effectively the former could introduce an
unnecessary move constructor call in some contexts.  In C++20 and earlier
we could emulate auto(x) with decay_t<decltype((x))>(x).

After this paper the only remaining uses of decay-copy in the library
are in the specification of some range adaptors.  In our implementation
of those range adaptors I believe decay-copy is already implied which
is why we don't mirror the wording and use __decay_copy explicitly.  So
since it's apparently no longer needed this patch goes ahead and removes
__decay_copy.

libstdc++-v3/ChangeLog:

	* c++config (_GLIBCXX_AUTO_CAST): Define.
	* include/bits/iterator_concepts.h (_Decay_copy, __decay_copy):
	Remove.
	(__member_begin, __adl_begin): Use _GLIBCXX_AUTO_CAST instead of
	__decay_copy as per P0849R8.
	* include/bits/ranges_base.h (_Begin): Likewise.
	(__member_end, __adl_end, _End): Likewise.
	(__member_rbegin, __adl_rbegin, _RBegin): Likewise.
	(__member_rend, __adl_rend, _Rend): Likewise.
	(__member_size, __adl_size, _Size): Likewise.
	(_Data): Likewise.
---
 libstdc++-v3/include/bits/c++config           |  6 +++
 libstdc++-v3/include/bits/iterator_concepts.h | 13 +-----
 libstdc++-v3/include/bits/ranges_base.h       | 40 +++++++++----------
 3 files changed, 28 insertions(+), 31 deletions(-)

diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
index 29d795f687c..fdbf90e28fc 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -265,6 +265,12 @@
 #define _GLIBCXX_NOEXCEPT_QUAL
 #endif
 
+#if __cpp_auto_cast
+# define _GLIBCXX_AUTO_CAST(X) auto(X)
+#else
+# define _GLIBCXX_AUTO_CAST(X) ::std::__decay_t<decltype((X))>(X)
+#endif
+
 // Macro for extern template, ie controlling template linkage via use
 // of extern keyword on template declaration. As documented in the g++
 // manual, it inhibits all implicit instantiations and is used
diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h
index 490a362cdf1..0fcfed56737 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -1003,19 +1003,10 @@ namespace ranges
   {
     using std::__detail::__class_or_enum;
 
-    struct _Decay_copy final
-    {
-      template<typename _Tp>
-	constexpr decay_t<_Tp>
-	operator()(_Tp&& __t) const
-	noexcept(is_nothrow_convertible_v<_Tp, decay_t<_Tp>>)
-	{ return std::forward<_Tp>(__t); }
-    } inline constexpr __decay_copy{};
-
     template<typename _Tp>
       concept __member_begin = requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.begin()) } -> input_or_output_iterator;
+	  { _GLIBCXX_AUTO_CAST(__t.begin()) } -> input_or_output_iterator;
 	};
 
     // Poison pill so that unqualified lookup doesn't find std::begin.
@@ -1025,7 +1016,7 @@ namespace ranges
       concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(begin(__t)) } -> input_or_output_iterator;
+	  { _GLIBCXX_AUTO_CAST(begin(__t)) } -> input_or_output_iterator;
 	};
 
     // Simplified version of std::ranges::begin that only supports lvalues,
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index cb2eba1f841..80ff1e300ce 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -115,9 +115,9 @@ namespace ranges
 	  if constexpr (is_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_begin<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().begin()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().begin()));
 	  else
-	    return noexcept(__decay_copy(begin(std::declval<_Tp&>())));
+	    return noexcept(_GLIBCXX_AUTO_CAST(begin(std::declval<_Tp&>())));
 	}
 
     public:
@@ -142,7 +142,7 @@ namespace ranges
     template<typename _Tp>
       concept __member_end = requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>;
+	  { _GLIBCXX_AUTO_CAST(__t.end()) } -> sentinel_for<__range_iter_t<_Tp>>;
 	};
 
     // Poison pill so that unqualified lookup doesn't find std::end.
@@ -152,7 +152,7 @@ namespace ranges
       concept __adl_end = __class_or_enum<remove_reference_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>;
+	  { _GLIBCXX_AUTO_CAST(end(__t)) } -> sentinel_for<__range_iter_t<_Tp>>;
 	};
 
     struct _End
@@ -165,9 +165,9 @@ namespace ranges
 	  if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_end<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().end()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().end()));
 	  else
-	    return noexcept(__decay_copy(end(std::declval<_Tp&>())));
+	    return noexcept(_GLIBCXX_AUTO_CAST(end(std::declval<_Tp&>())));
 	}
 
     public:
@@ -192,7 +192,7 @@ namespace ranges
     template<typename _Tp>
       concept __member_rbegin = requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.rbegin()) } -> input_or_output_iterator;
+	  { _GLIBCXX_AUTO_CAST(__t.rbegin()) } -> input_or_output_iterator;
 	};
 
     void rbegin() = delete;
@@ -201,7 +201,7 @@ namespace ranges
       concept __adl_rbegin = __class_or_enum<remove_reference_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(rbegin(__t)) } -> input_or_output_iterator;
+	  { _GLIBCXX_AUTO_CAST(rbegin(__t)) } -> input_or_output_iterator;
 	};
 
     template<typename _Tp>
@@ -219,9 +219,9 @@ namespace ranges
 	_S_noexcept()
 	{
 	  if constexpr (__member_rbegin<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().rbegin()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rbegin()));
 	  else if constexpr (__adl_rbegin<_Tp>)
-	    return noexcept(__decay_copy(rbegin(std::declval<_Tp&>())));
+	    return noexcept(_GLIBCXX_AUTO_CAST(rbegin(std::declval<_Tp&>())));
 	  else
 	    {
 	      if constexpr (noexcept(_End{}(std::declval<_Tp&>())))
@@ -254,7 +254,7 @@ namespace ranges
     template<typename _Tp>
       concept __member_rend = requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.rend()) }
+	  { _GLIBCXX_AUTO_CAST(__t.rend()) }
 	    -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
 	};
 
@@ -264,7 +264,7 @@ namespace ranges
       concept __adl_rend = __class_or_enum<remove_reference_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(rend(__t)) }
+	  { _GLIBCXX_AUTO_CAST(rend(__t)) }
 	    -> sentinel_for<decltype(_RBegin{}(std::forward<_Tp>(__t)))>;
 	};
 
@@ -276,9 +276,9 @@ namespace ranges
 	_S_noexcept()
 	{
 	  if constexpr (__member_rend<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().rend()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().rend()));
 	  else if constexpr (__adl_rend<_Tp>)
-	    return noexcept(__decay_copy(rend(std::declval<_Tp&>())));
+	    return noexcept(_GLIBCXX_AUTO_CAST(rend(std::declval<_Tp&>())));
 	  else
 	    {
 	      if constexpr (noexcept(_Begin{}(std::declval<_Tp&>())))
@@ -312,7 +312,7 @@ namespace ranges
       concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.size()) } -> __detail::__is_integer_like;
+	  { _GLIBCXX_AUTO_CAST(__t.size()) } -> __detail::__is_integer_like;
 	};
 
     void size() = delete;
@@ -322,7 +322,7 @@ namespace ranges
 	&& !disable_sized_range<remove_cvref_t<_Tp>>
 	&& requires(_Tp& __t)
 	{
-	  { __decay_copy(size(__t)) } -> __detail::__is_integer_like;
+	  { _GLIBCXX_AUTO_CAST(size(__t)) } -> __detail::__is_integer_like;
 	};
 
     template<typename _Tp>
@@ -347,9 +347,9 @@ namespace ranges
 	  if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
 	    return true;
 	  else if constexpr (__member_size<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().size()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().size()));
 	  else if constexpr (__adl_size<_Tp>)
-	    return noexcept(__decay_copy(size(std::declval<_Tp&>())));
+	    return noexcept(_GLIBCXX_AUTO_CAST(size(std::declval<_Tp&>())));
 	  else if constexpr (__sentinel_size<_Tp>)
 	    return noexcept(_End{}(std::declval<_Tp&>())
 			    - _Begin{}(std::declval<_Tp&>()));
@@ -459,7 +459,7 @@ namespace ranges
     template<typename _Tp>
       concept __member_data = requires(_Tp& __t)
 	{
-	  { __decay_copy(__t.data()) } -> __pointer_to_object;
+	  { _GLIBCXX_AUTO_CAST(__t.data()) } -> __pointer_to_object;
 	};
 
     template<typename _Tp>
@@ -473,7 +473,7 @@ namespace ranges
 	_S_noexcept()
 	{
 	  if constexpr (__member_data<_Tp>)
-	    return noexcept(__decay_copy(std::declval<_Tp&>().data()));
+	    return noexcept(_GLIBCXX_AUTO_CAST(std::declval<_Tp&>().data()));
 	  else
 	    return noexcept(_Begin{}(std::declval<_Tp&>()));
 	}
-- 
2.47.0.rc1.33.g90fe3800b9



More information about the Libstdc++ mailing list