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
>