On Fri, 23 Oct 2026 at 22:08, Nathan Myers <[email protected]> wrote:
>
> Constructing a string from a streambuf iterator pair or range has
> been found to be 3x faster using the streambuf member `sgetn`.
> This is the minimal implementation as outlined in [PR112925], not
> checking for an underlying filebuf.
>
> libstdc++-v3/ChangeLog:
>         PR libstdc++/112925
>         * include/bits/basic_string.h: (basic_string range ctor)
>         Special-case ranges of streambuf iterators.
>         * include/bits/basic_string.tcc: (_M_construct) Special-case
>         streambuf iterators. These always consume the whole stream.
>         * testsuite/21_strings/basic_string/cons/from_range2.cc:
>         New test.
> ---
>  libstdc++-v3/include/bits/basic_string.h      |  6 +++-
>  libstdc++-v3/include/bits/basic_string.tcc    | 15 ++++++++++
>  .../basic_string/cons/from_range2.cc          | 29 +++++++++++++++++++
>  3 files changed, 49 insertions(+), 1 deletion(-)
>  create mode 100644 
> libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
>
> diff --git a/libstdc++-v3/include/bits/basic_string.h 
> b/libstdc++-v3/include/bits/basic_string.h
> index fd9419220a4..513ea274ca7 100644
> --- a/libstdc++-v3/include/bits/basic_string.h
> +++ b/libstdc++-v3/include/bits/basic_string.h
> @@ -826,7 +826,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>         basic_string(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
>         : basic_string(__a)
>         {
> -         if constexpr (ranges::forward_range<_Rg> || 
> ranges::sized_range<_Rg>)
> +         using _SbufIter = istreambuf_iterator<_CharT, char_traits<_CharT>>;
> +         if constexpr(is_same_v<ranges::iterator_t<_Rg>, _SbufIter>)
> +           _M_construct(__rg.begin(), _SbufIter(), input_iterator_tag{});
> +         else if constexpr (
> +             ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
>             {
>               const auto __n = static_cast<size_type>(ranges::distance(__rg));
>               reserve(__n);
> diff --git a/libstdc++-v3/include/bits/basic_string.tcc 
> b/libstdc++-v3/include/bits/basic_string.tcc
> index de82e997888..d68a4492ef0 100644
> --- a/libstdc++-v3/include/bits/basic_string.tcc
> +++ b/libstdc++-v3/include/bits/basic_string.tcc
> @@ -199,6 +199,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>         _M_init_local_buf();
>
> +       // NB: size() (i.e. _M_length) is initialized only at the end.
>         while (__beg != __end && __len < __capacity)
>           {
>             _M_local_buf[__len++] = *__beg;
> @@ -228,6 +229,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>                 _M_data(__another.__ptr);
>                 _M_capacity(__capacity);
>               }
> +
> +#if __cplusplus >= 201103L
> +           using _SbufIter = istreambuf_iterator<_CharT, 
> char_traits<_CharT>>;
> +           if _GLIBCXX17_CONSTEXPR (is_same<_InIterator, _SbufIter>::value)

This should be just 'if constexpr', not using the macro. We can use
diagnostic pragmas to disable the warnings so we can use 'if
constexpr' in C++11.

> +             {
> +               if (!std::__is_constant_evaluated())
> +                {
> +                  auto __r = std::__copy_n_a(__beg, __capacity - __len,
> +                                             data() + __len, false);
> +                  __len = __r - data();
> +                  continue;
> +                }
> +             }
> +#endif
>             traits_type::assign(_M_data()[__len++],
>                                 static_cast<_CharT>(*__beg));
>             ++__beg;
> diff --git 
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc 
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
> new file mode 100644
> index 00000000000..33a654f35af
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
> @@ -0,0 +1,29 @@
> +// { dg-do run { target c++23 } }

This tests the C++23 constructor but we should have another test for
C++11 and later using the string(i, i) constructor.

> +
> +#include <string>
> +#include <sstream>
> +#include <streambuf>
> +#include <ranges>
> +#include <testsuite_hooks.h>
> +
> +struct istreambuf_view : std::ranges::view_interface<istreambuf_view> {
> +  std::istream& str;
> +  istreambuf_view(std::istream& str) : str(str) {}
> +  auto begin() { return std::istreambuf_iterator<char>(str); }
> +  auto end() { return std::istreambuf_iterator<char>(); }
> +};
> +
> +constexpr void

I don't think this can be constexpr

> +do_test()
> +{
> +  std::string abcs{"abcdefghijklmnopqrstuvwxyz"};
> +  std::istringstream stream{abcs + abcs};
> +  istreambuf_view range(stream);
> +  std::basic_string str{std::from_range, range};
> +  VERIFY(str == abcs + abcs);
> +}
> +
> +int main()
> +{
> +  do_test();
> +}
> --
> 2.56.0
>

Reply via email to