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.