From 52fbb738cba35a709ac10871b69575647675ae19 Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Wed, 8 Apr 2026 09:19:49 +0800 Subject: [PATCH] Implement LWG-3662 string::append/assign(NTBS, pos, n) should not construct a temporary string --- stl/inc/xstring | 20 +++++++++ .../tests/VSO_0174871_string_replace/test.cpp | 45 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/stl/inc/xstring b/stl/inc/xstring index 8ba06e0de21..eebd69c0543 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1556,6 +1556,16 @@ public: } #endif // _HAS_CXX17 + _CONSTEXPR20 basic_string& append( + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count) { + // append(string_view(_Ptr).substr(_Off, _Count)) + const size_type _Length = _Convert_size(_Traits::length(_Ptr)); + if (_Off > _Length) { + _Scary_val::_Xran(); + } + return _Append(_Ptr + _Off, (_STD min) (_Length - _Off, _Count)); + } + _CONSTEXPR20 basic_string& append( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count) @@ -1645,6 +1655,16 @@ public: } #endif // _HAS_CXX17 + _CONSTEXPR20 basic_string& assign( + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count) { + // assign(string_view(_Ptr).substr(_Off, _Count)) + const size_type _Length = _Convert_size(_Traits::length(_Ptr)); + if (_Off > _Length) { + _Scary_val::_Xran(); + } + return _Assign(_Ptr + _Off, (_STD min) (_Length - _Off, _Count)); + } + _CONSTEXPR20 basic_string& assign( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) diff --git a/tests/std/tests/VSO_0174871_string_replace/test.cpp b/tests/std/tests/VSO_0174871_string_replace/test.cpp index e06149b2ef1..ef16bbfbdd4 100644 --- a/tests/std/tests/VSO_0174871_string_replace/test.cpp +++ b/tests/std/tests/VSO_0174871_string_replace/test.cpp @@ -103,6 +103,50 @@ void test_shrink_to_fit() { assert(example == longerStr); } +template +struct CharAllocator { + using value_type = T; + + CharAllocator() = delete; + explicit CharAllocator(int) noexcept {} + template + CharAllocator(const CharAllocator&) noexcept {} + + T* allocate(std::size_t n) { + return new T[n]; + } + void deallocate(T* p, std::size_t) noexcept { + delete[] p; + } +}; + +void test_LWG3662() { + // append/assign(NTBS, pos, n) should not construct a temporary string + basic_string, CharAllocator> s(CharAllocator(0)); + + s.append("hello", 1, 3); + assert(s == "ell"); + s.assign("world", 1, 3); + assert(s == "orl"); + + s.clear(); + + try { + s.append("hello", 10, 1); + puts("append with out-of-range position should throw"); + abort(); + } catch (const out_of_range&) { + // purposely do nothing on out_of_range + } + try { + s.assign("world", 10, 1); + puts("assign with out-of-range position should throw"); + abort(); + } catch (const out_of_range&) { + // purposely do nothing on out_of_range + } +} + int main() { // Plain replacements with shrinking / same size / growing test_replace(3, 3, "ab", "012ab6789"); @@ -142,4 +186,5 @@ int main() { test_index_boundary_cases(); test_shrink_to_fit(); + test_LWG3662(); }