https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111052
--- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> --- (In reply to Jonathan Wakely from comment #1) > We should also make ranges::copy implement the std::copy optimization for > copying to ostreambuf_iterator, which is an important performance > enhancement. That will benefit: > > std::format_to(std::ostreambuf_iterator<char>(std::cout), ""); The following does that, but only gives about 10% improvement: --- a/libstdc++-v3/include/bits/ranges_algobase.h +++ b/libstdc++-v3/include/bits/ranges_algobase.h @@ -201,6 +201,22 @@ namespace ranges copy_backward_result<_Iter, _Out>> __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result); +namespace __detail +{ + // True if _Tp is std::istreambuf_iterator<C>. + template<typename _Tp> constexpr bool __is_istreambuf_iterator = false; + template<typename _CharT> + constexpr bool + __is_istreambuf_iterator<istreambuf_iterator<_CharT, char_traits<_CharT>>> + = true; + // True if _Tp is std::ostreambuf_iterator<C>. + template<typename _Tp> constexpr bool __is_ostreambuf_iterator = false; + template<typename _CharT> + constexpr bool + __is_ostreambuf_iterator<ostreambuf_iterator<_CharT, char_traits<_CharT>>> + = true; +} // namespace __detail + template<bool _IsMove, input_iterator _Iter, sentinel_for<_Iter> _Sent, weakly_incrementable _Out> @@ -217,6 +233,8 @@ namespace ranges using __detail::__is_move_iterator; using __detail::__is_reverse_iterator; using __detail::__is_normal_iterator; + using __detail::__is_istreambuf_iterator; + using __detail::__is_ostreambuf_iterator; if constexpr (__is_move_iterator<_Iter> && same_as<_Iter, _Sent>) { auto [__in, __out] @@ -248,6 +266,34 @@ namespace ranges = ranges::__copy_or_move<_IsMove>(std::move(__first), __last, __result.base()); return {std::move(__in), decltype(__result){__out}}; } + else if constexpr (is_pointer_v<_Iter> && is_pointer_v<_Sent> + && __is_ostreambuf_iterator<_Out> + && requires { + requires is_same_v<iter_value_t<_Iter>, typename _Out::char_type>; + requires is_same_v<iter_value_t<_Iter>, iter_value_t<_Sent>>; + }) + { + // copy([const] C*, [const] C*, ostreambuf_iterator<C>) + return {__first, std::__copy_move_a2<_IsMove>(__first, __last, __result)}; + } + else if constexpr (__is_istreambuf_iterator<_Out> + && is_same_v<_Out, iter_value_t<_Iter>*> + && is_same_v<_Iter, _Sent>) + { + // copy(istreambuf_iterator<C>, istreambuf_iterator<C>, C*) + return {__first, std::__copy_move_a2<_IsMove>(__first, __last, __result)}; + } + else if constexpr (__is_istreambuf_iterator<_Iter> && is_same_v<_Iter, _Sent> + && !_IsMove && __is_ostreambuf_iterator<_Out> + && requires { + requires is_same_v<typename _Iter::char_type, + typename _Out::char_type>; + }) + { + // copy(istreambuf_iterator<C>, istreambuf_iterator<C>, + // ostreambuf_iterator<C>) + return {__first, std::copy(__first, __last, __result)}; + } else if constexpr (sized_sentinel_for<_Sent, _Iter>) { if (!std::__is_constant_evaluated()) > The _Iter_sink<_CharT, _Iter> could recognise an ostreambuf_iterator and use > std::copy, but we should really just make ranges::copy do those > optimizations. Better would be for _Iter_sink<_CharT, ostreambuf_iterator<_CharT>> to allow writing directly to the streambuf's put area. That should be faster than writing to the sink's buffer and then copying it to the streambuf.