diff --git a/stl/inc/vector b/stl/inc/vector index 780b9497cf1..9a943420d43 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -126,7 +126,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_const_iterator operator+(const difference_type _Off) const noexcept { _Vector_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vector_const_iterator& operator-=(const difference_type _Off) noexcept { @@ -135,7 +136,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_const_iterator operator-(const difference_type _Off) const noexcept { _Vector_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _Vector_const_iterator& _Right) const noexcept { @@ -307,7 +309,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator+(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vector_iterator& operator-=(const difference_type _Off) noexcept { @@ -319,7 +322,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator-(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { @@ -2185,7 +2189,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator+(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator-=(const difference_type _Off) noexcept { @@ -2194,7 +2199,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator-(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _Vb_const_iterator& _Right) const noexcept { @@ -2363,7 +2369,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator+(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vb_iterator& operator-=(const difference_type _Off) noexcept { @@ -2375,7 +2382,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator-(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 693c4f7fe09..95b331f12f0 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1178,8 +1178,8 @@ public: static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; - _Container_proxy* _Myproxy = nullptr; - _Iterator_base12* _Mynextiter = nullptr; + mutable _Container_proxy* _Myproxy = nullptr; + mutable _Iterator_base12* _Mynextiter = nullptr; #if _ITERATOR_DEBUG_LEVEL == 2 private: @@ -1338,7 +1338,8 @@ struct _Container_proxy_ptr12 : _Basic_container_proxy_ptr12 { }; #if _ITERATOR_DEBUG_LEVEL == 0 -#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_allocator() +_INLINE_VAR constexpr _Fake_allocator _Fake_alloc{}; +#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_alloc // TRANSITION, VSO-1284799, should be _Fake_allocator{} template using _Container_proxy_ptr = _Fake_proxy_ptr_impl; #else // _ITERATOR_DEBUG_LEVEL == 0 diff --git a/stl/inc/xstring b/stl/inc/xstring index 8551a274080..9e63cab02b2 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -70,8 +70,8 @@ struct _Char_traits { // properties of a string or stream element _Pre_satisfies_(_Dest_size >= _Count) static _CONSTEXPR20 _Elem* _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _First1, - const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, - const size_t _Count) noexcept { // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) + const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept { + // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) _STL_VERIFY(_Count <= _Dest_size, "invalid argument"); return copy(_First1, _First2, _Count); } @@ -164,18 +164,33 @@ struct _Char_traits { // properties of a string or stream element } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) - for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { - *_Next = _Ch; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + _STD construct_at(_Next, _Ch); + } + } else +#endif // __cpp_lib_constexpr_string + { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + *_Next = _Ch; + } } return _First; } static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { - _Left = _Right; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _STD construct_at(_STD addressof(_Left), _Right); + } else +#endif // __cpp_lib_constexpr_string + { + _Left = _Right; + } } _NODISCARD static constexpr bool eq(const _Elem& _Left, const _Elem& _Right) noexcept { @@ -271,8 +286,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -421,8 +435,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -1894,13 +1907,17 @@ public: using pointer = typename _Mystr::const_pointer; using reference = const value_type&; - _String_const_iterator() noexcept : _Ptr() {} + _CONSTEXPR20_CONTAINER _String_const_iterator() noexcept : _Ptr() {} - _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept : _Ptr(_Parg) { + _CONSTEXPR20_CONTAINER _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept + : _Ptr(_Parg) { this->_Adopt(_Pstring); } - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_const_iterator& operator=(const _String_const_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1916,11 +1933,11 @@ public: return *_Ptr; } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_const_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator++() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot increment value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1933,13 +1950,13 @@ public: return *this; } - _String_const_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator++(int) noexcept { _String_const_iterator _Tmp = *this; ++*this; return _Tmp; } - _String_const_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator--() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot decrement value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1952,13 +1969,13 @@ public: return *this; } - _String_const_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator--(int) noexcept { _String_const_iterator _Tmp = *this; --*this; return _Tmp; } - void _Verify_offset(const difference_type _Off) const noexcept { + _CONSTEXPR20_CONTAINER void _Verify_offset(const difference_type _Off) const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Off == 0) { return; @@ -1985,7 +2002,7 @@ public: #endif // _ITERATOR_DEBUG_LEVEL >= 1 } - _String_const_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _Verify_offset(_Off); #endif // _ITERATOR_DEBUG_LEVEL >= 1 @@ -1993,36 +2010,38 @@ public: return *this; } - _NODISCARD _String_const_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator+(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_const_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } - _NODISCARD _String_const_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator-(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD difference_type operator-(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr - _Right._Ptr; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } - _NODISCARD bool operator==(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr == _Right._Ptr; } #if _HAS_CXX20 - _NODISCARD strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); } @@ -2049,7 +2068,8 @@ public: } #endif // !_HAS_CXX20 - void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair + _CONSTEXPR20_CONTAINER void _Compat(const _String_const_iterator& _Right) const noexcept { + // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "string iterators incompatible (e.g." " point to different string instances)"); @@ -2059,7 +2079,8 @@ public: } #if _ITERATOR_DEBUG_LEVEL >= 1 - friend void _Verify_range(const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { + friend _CONSTEXPR20_CONTAINER void _Verify_range( + const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { _STL_VERIFY(_First._Getcont() == _Last._Getcont(), "string iterators in range are from different containers"); _STL_VERIFY(_First._Ptr <= _Last._Ptr, "string iterator range transposed"); } @@ -2067,11 +2088,11 @@ public: using _Prevent_inheriting_unwrap = _String_const_iterator; - _NODISCARD const value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const value_type* _Unwrapped() const noexcept { return _Unfancy(_Ptr); } - void _Seek_to(const value_type* _It) noexcept { + _CONSTEXPR20_CONTAINER void _Seek_to(const value_type* _It) noexcept { _Ptr = _Refancy(const_cast(_It)); } @@ -2079,7 +2100,7 @@ public: }; template -_NODISCARD _String_const_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator<_Mystr> operator+( typename _String_const_iterator<_Mystr>::difference_type _Off, _String_const_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2132,71 +2153,76 @@ public: using _Mybase::_Mybase; - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_iterator& operator=(const _String_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } - _String_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator++(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } - _String_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } - _String_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator--(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } - _String_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } - _NODISCARD _String_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator+(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; - _NODISCARD _String_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator-(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } using _Prevent_inheriting_unwrap = _String_iterator; - _NODISCARD value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER value_type* _Unwrapped() const noexcept { return const_cast(_Unfancy(this->_Ptr)); } }; template -_NODISCARD _String_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_iterator<_Mystr> operator+( typename _String_iterator<_Mystr>::difference_type _Off, _String_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2255,7 +2281,7 @@ public: using reference = value_type&; using const_reference = const value_type&; - _String_val() noexcept : _Bx(), _Mysize(0), _Myres(0) {} + _CONSTEXPR20_CONTAINER _String_val() noexcept : _Bx() {} // length of internal buffer, [1, 16]: static constexpr size_type _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type); @@ -2266,7 +2292,7 @@ public: : sizeof(value_type) <= 8 ? 1 : 0; - value_type* _Myptr() noexcept { + _CONSTEXPR20_CONTAINER value_type* _Myptr() noexcept { value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2275,7 +2301,7 @@ public: return _Result; } - const value_type* _Myptr() const noexcept { + _CONSTEXPR20_CONTAINER const value_type* _Myptr() const noexcept { const value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2284,17 +2310,24 @@ public: return _Result; } - bool _Large_string_engaged() const noexcept { + _CONSTEXPR20_CONTAINER bool _Large_string_engaged() const noexcept { +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + return true; + } +#endif // __cpp_lib_constexpr_string return _BUF_SIZE <= _Myres; } - void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] + _CONSTEXPR20_CONTAINER void _Check_offset(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()] if (_Mysize < _Off) { _Xran(); } } - void _Check_offset_exclusive(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()) + _CONSTEXPR20_CONTAINER void _Check_offset_exclusive(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()) if (_Mysize <= _Off) { _Xran(); } @@ -2304,23 +2337,23 @@ public: _Xout_of_range("invalid string position"); } - size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { + _CONSTEXPR20_CONTAINER size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { // trims _Size to the longest it can be assuming a string at/after _Off return (_STD min)(_Size, _Mysize - _Off); } union _Bxty { // storage for small buffer or pointer to larger one - _Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER _Bxty() noexcept : _Ptr() {} // user-provided, for fancy pointers - ~_Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER ~_Bxty() noexcept {} // user-provided, for fancy pointers value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr) } _Bx; - size_type _Mysize; // current length of string - size_type _Myres; // current storage reserved for string + size_type _Mysize = 0; // current length of string + size_type _Myres = 0; // current storage reserved for string }; // CLASS TEMPLATE basic_string @@ -2414,7 +2447,7 @@ private: #endif // _HAS_CXX17 public: - basic_string(const basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2422,24 +2455,27 @@ public: _Proxy._Release(); } - basic_string(const basic_string& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Construct_lv_contents(_Right); _Proxy._Release(); } - basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) + : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER explicit basic_string(const _Alloc& _Al) noexcept + : _Mypair(_One_then_variadic_args_t{}, _Al) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, ) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2448,7 +2484,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const basic_string& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2458,7 +2494,8 @@ public: _Proxy._Release(); } - basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) + _CONSTEXPR20_CONTAINER basic_string( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2467,7 +2504,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2477,7 +2514,7 @@ public: _Proxy._Release(); } - basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2488,7 +2525,8 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2496,7 +2534,8 @@ public: _Proxy._Release(); } - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) + : _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2508,7 +2547,7 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Count * _Ch with allocator auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2518,7 +2557,8 @@ public: } template , int> = 0> - basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2528,7 +2568,7 @@ public: } template - void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { // initialize from [_First, _Last), input iterators _Tidy_deallocate_guard _Guard{this}; for (; _First != _Last; ++_First) { @@ -2539,33 +2579,35 @@ public: } template - void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { // initialize from [_First, _Last), forward iterators const size_type _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); reserve(_Count); _Construct(_First, _Last, input_iterator_tag{}); } - void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - void _Construct(const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct( + const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), const pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - basic_string(basic_string&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right) noexcept + : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( _Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2578,12 +2620,13 @@ public: } } - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, const _Elem* const _Left_ptr, - const size_type _Left_size, const _Elem* const _Right_ptr, const size_type _Right_size) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, + const _Elem* const _Left_ptr, const size_type _Left_size, const _Elem* const _Right_ptr, + const size_type _Right_size) : _Mypair( _One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Source_of_al._Getal())) { _STL_INTERNAL_CHECK(_Left_size <= max_size()); @@ -2595,13 +2638,28 @@ public: _Elem* _Ptr = _My_data._Bx._Buf; auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws - if (_New_capacity < _New_size) { - _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, max_size()); - const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws - _Ptr = _Unfancy(_Fancyptr); + +#ifdef __cpp_lib_constexpr_string + const bool _Activate_large_mode = _New_capacity < _New_size || _STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Activate_large_mode = _New_capacity < _New_size; +#endif // __cpp_lib_constexpr_string + + if (_Activate_large_mode) { + // we should never allocate less than _BUF_SIZE space (_New_size could be small if constant evaluated) + const size_type _Requested_size = (_STD max)(_New_size, _BUF_SIZE); + _New_capacity = _Calculate_growth(_Requested_size, _BUF_SIZE - 1, max_size()); + const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws + _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); } +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Ptr, _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Traits::copy(_Ptr, _Left_ptr, _Left_size); @@ -2610,7 +2668,7 @@ public: _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Left._Getal()) { auto& _My_data = _Mypair._Myval2; auto& _Left_data = _Left._Mypair._Myval2; @@ -2628,7 +2686,7 @@ public: if (_Fits_in_left && _Right_capacity <= _Left_capacity) { // take _Left's buffer, max_size() is OK because _Fits_in_left _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Left, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); _My_data._Mysize = _New_size; @@ -2648,7 +2706,7 @@ public: // therefore: _Right must have more than the minimum capacity, so it must be _Large_string_engaged() _STL_INTERNAL_CHECK(_Right_data._Large_string_engaged()); _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); _Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); @@ -2667,6 +2725,11 @@ public: _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws // nothrow hereafter +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_Fancyptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -2678,7 +2741,7 @@ public: #if _HAS_CXX17 template = 0> - explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2688,7 +2751,7 @@ public: } template = 0> - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) using _Al auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2702,19 +2765,20 @@ public: #if _HAS_CXX20 basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al) : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) { + // Used exclusively by basic_stringbuf _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } #endif // _HAS_CXX20 private: - void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { _Tidy_deallocate(); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2723,11 +2787,11 @@ private: _Mypair._Myval2._Reload_proxy( _GET_PROXY_ALLOCATOR(_Alty, _Getal()), _GET_PROXY_ALLOCATOR(_Alty, _Right._Getal())); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } } - void _Move_assign(basic_string& _Right, _No_propagate_allocators) { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _No_propagate_allocators) { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2737,8 +2801,8 @@ private: public: #if _HAS_CXX20 - bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { - // Move assign from a buffer, used by basic_stringbuf; returns _Large_string_engaged() + _NODISCARD bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { + // Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_string_engaged() _Tidy_deallocate(); pointer _Fancy_right = _Refancy(_Right); auto& _My_data = _Mypair._Myval2; @@ -2762,7 +2826,7 @@ public: }; _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { - // Release to a buffer, or allocate a new one if in small string mode + // Release to a buffer, or allocate a new one if in small string mode; used exclusively by basic_stringbuf _Released_buffer _Result; auto& _My_data = _Mypair._Myval2; _Result._Size = _My_data._Mysize; @@ -2781,7 +2845,8 @@ public: } #endif // _HAS_CXX20 - basic_string& operator=(basic_string&& _Right) noexcept(noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { + _CONSTEXPR20_CONTAINER basic_string& operator=(basic_string&& _Right) noexcept( + noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { if (this != _STD addressof(_Right)) { _Move_assign(_Right, _Choose_pocma<_Alty>{}); } @@ -2789,7 +2854,7 @@ public: return *this; } - basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { + _CONSTEXPR20_CONTAINER basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { *this = _STD move(_Right); return *this; } @@ -2804,33 +2869,35 @@ private: _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); } - void _Take_contents(basic_string& _Right, true_type) noexcept { - // assign by stealing _Right's buffer, memcpy optimization - // pre: this != &_Right - // pre: allocator propagation (POCMA) from _Right, if necessary, is complete - // pre: *this owns no memory, iterators orphaned (note: - // _Buf/_Ptr/_Mysize/_Myres may be garbage init) -#if _ITERATOR_DEBUG_LEVEL != 0 - if (_Right._Mypair._Myval2._Large_string_engaged()) { - // take ownership of _Right's iterators along with its buffer - _Swap_proxy_and_iterators(_Right); - } else { - _Right._Mypair._Myval2._Orphan_all(); - } -#endif // _ITERATOR_DEBUG_LEVEL != 0 - - _Memcpy_val_from(_Right); - _Right._Tidy_init(); - } - - void _Take_contents(basic_string& _Right, false_type) noexcept { - // assign by stealing _Right's buffer, general case + _CONSTEXPR20_CONTAINER void _Take_contents(basic_string& _Right) noexcept { + // assign by stealing _Right's buffer // pre: this != &_Right // pre: allocator propagation (POCMA) from _Right, if necessary, is complete // pre: *this owns no memory, iterators orphaned // (note: _Buf/_Ptr/_Mysize/_Myres may be garbage init) auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { +#if _ITERATOR_DEBUG_LEVEL != 0 + if (_Right_data._Large_string_engaged()) { + // take ownership of _Right's iterators along with its buffer + _Swap_proxy_and_iterators(_Right); + } else { + _Right_data._Orphan_all(); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + _Memcpy_val_from(_Right); + _Right._Tidy_init(); + return; + } + } + if (_Right_data._Large_string_engaged()) { // steal buffer _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); _Right_data._Bx._Ptr = nullptr; @@ -2845,7 +2912,7 @@ private: _Right._Tidy_init(); } - void _Construct_lv_contents(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Construct_lv_contents(const basic_string& _Right) { // assign by copying data stored in _Right // pre: this != &_Right // pre: *this owns no memory, iterators orphaned (note: @@ -2854,7 +2921,16 @@ private: const size_type _Right_size = _Right_data._Mysize; const _Elem* const _Right_ptr = _Right_data._Myptr(); auto& _My_data = _Mypair._Myval2; - if (_Right_size < _BUF_SIZE) { // stay small, don't allocate + +#ifdef __cpp_lib_constexpr_string + const bool _Stay_small = _Right_size < _BUF_SIZE && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Stay_small = _Right_size < _BUF_SIZE; +#endif // __cpp_lib_constexpr_string + + // NOTE: even if _Right is in large mode, we only go into large mode ourselves if the actual size of _Right + // requires it + if (_Stay_small) { // stay small, don't allocate _Traits::copy(_My_data._Bx._Buf, _Right_ptr, _BUF_SIZE); _My_data._Mysize = _Right_size; _My_data._Myres = _BUF_SIZE - 1; @@ -2865,13 +2941,19 @@ private: const size_type _New_capacity = (_STD min)(_Right_size | _ALLOC_MASK, max_size()); const pointer _New_array = _Al.allocate(_New_capacity + 1); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_array); + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_array), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Traits::copy(_Unfancy(_New_array), _Right_ptr, _Right_size + 1); _My_data._Mysize = _Right_size; _My_data._Myres = _New_capacity; } public: - basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) + _CONSTEXPR20_CONTAINER basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2880,23 +2962,23 @@ public: _Proxy._Release(); } - basic_string& operator=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator=(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& operator+=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& assign(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& assign(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& append(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& append(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2905,7 +2987,7 @@ public: return begin() + static_cast(_Off); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const initializer_list<_Elem> _Ilist) { // replace with initializer_list _Adl_verify_range(_First, _Last); @@ -2917,7 +2999,7 @@ public: return replace(_Offset, _Length, _Ilist.begin(), _Convert_size(_Ilist.size())); } - ~basic_string() noexcept { + _CONSTEXPR20_CONTAINER ~basic_string() noexcept { _Tidy_deallocate(); #if _ITERATOR_DEBUG_LEVEL != 0 auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2932,23 +3014,28 @@ public: private: void _Copy_assign_val_from_small(const basic_string& _Right) { // TRANSITION, VSO-761321; inline into only caller when that's fixed +#ifdef __cpp_lib_constexpr_string + _STL_ASSERT(!_STD is_constant_evaluated(), "SSO should be disabled in a constexpr context"); +#endif // __cpp_lib_constexpr_string _Tidy_deallocate(); if constexpr (_Can_memcpy_val) { _Memcpy_val_from(_Right); } else { - _Traits::copy( - _Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Mysize + 1); - _Mypair._Myval2._Mysize = _Right._Mypair._Myval2._Mysize; - _Mypair._Myval2._Myres = _Right._Mypair._Myval2._Myres; + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); + _My_data._Mysize = _Right_data._Mysize; + _My_data._Myres = _Right_data._Myres; } } - void _Copy_assign(const basic_string& _Right, false_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, false_type) { _Pocca(_Getal(), _Right._Getal()); assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - void _Copy_assign(const basic_string& _Right, true_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, true_type) { auto& _Al = _Getal(); const auto& _Right_al = _Right._Getal(); if (_Al == _Right_al) { @@ -2965,6 +3052,13 @@ private: const auto _New_capacity = _Calculate_growth(_New_size, 0, _Right.max_size()); auto _Right_al_non_const = _Right_al; const auto _New_ptr = _Right_al_non_const.allocate(_New_capacity); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_size + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_Right._Mypair._Myval2._Bx._Ptr), _New_size + 1); _Tidy_deallocate(); _Mypair._Myval2._Bx._Ptr = _New_ptr; @@ -2979,7 +3073,7 @@ private: } public: - basic_string& operator=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const basic_string& _Right) { if (this != _STD addressof(_Right)) { _Copy_assign(_Right, _Choose_pocca<_Alty>{}); } @@ -2989,16 +3083,16 @@ public: #if _HAS_CXX17 template = 0> - basic_string& operator=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const _StringViewIsh& _Right) { return assign(_Right); } #endif // _HAS_CXX17 - basic_string& operator=(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& operator=(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr); } - basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} + _CONSTEXPR20_CONTAINER basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); @@ -3006,31 +3100,32 @@ public: return *this; } - basic_string& operator+=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const basic_string& _Right) { return append(_Right); } #if _HAS_CXX17 template = 0> - basic_string& operator+=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const _StringViewIsh& _Right) { return append(_Right); } #endif // _HAS_CXX17 - basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr); } - basic_string& operator+=(_Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(_Elem _Ch) { push_back(_Ch); return *this; } - basic_string& append(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const basic_string& _Right) { return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& append(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3039,20 +3134,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& append(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& append(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& append(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& append( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count) const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { @@ -3074,11 +3171,12 @@ public: _Ptr, _Count); } - basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // append _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { _Mypair._Myval2._Mysize = _Old_size + _Count; @@ -3100,7 +3198,8 @@ public: } template , int> = 0> - basic_string& append(const _Iter _First, const _Iter _Last) { // append [_First, _Last), input iterators + _CONSTEXPR20_CONTAINER basic_string& append(const _Iter _First, const _Iter _Last) { + // append [_First, _Last), input iterators _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3112,12 +3211,13 @@ public: } } - basic_string& assign(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const basic_string& _Right) { *this = _Right; return *this; } - basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3126,20 +3226,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& assign(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& assign(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& assign(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& assign( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3158,11 +3260,12 @@ public: _Ptr); } - basic_string& assign(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& assign(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; @@ -3181,7 +3284,7 @@ public: } template , int> = 0> - basic_string& assign(const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _Iter _First, const _Iter _Last) { _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3191,7 +3294,7 @@ public: basic_string _Right(_UFirst, _ULast, get_allocator()); if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) { _Mypair._Myval2._Orphan_all(); - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); return *this; } else { return assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); @@ -3199,11 +3302,12 @@ public: } } - basic_string& insert(const size_type _Off, const basic_string& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const basic_string& _Right) { + // insert _Right at _Off return insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off _Right._Mypair._Myval2._Check_offset(_Roff); @@ -3213,33 +3317,44 @@ public: #if _HAS_CXX17 template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { + // insert _Right at _Off const basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, - const size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + // insert _Right [_Roff, _Roff + _Count) at _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // insert [_Ptr, _Ptr + _Count) at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size; +#endif // __cpp_lib_constexpr_string + + if (_Check_overlap) { _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out, // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count size_type _Ptr_shifted_after; - if (_Ptr + _Count <= _Insert_at - || _Ptr > _Old_ptr + _Old_size) { // inserted content is before the shifted region, or does not alias + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + // inserted content is before the shifted region, or does not alias _Ptr_shifted_after = _Count; // none of _Ptr's data shifts } else if (_Insert_at <= _Ptr) { // all of [_Ptr, _Ptr + _Count) shifts _Ptr_shifted_after = 0; @@ -3265,11 +3380,13 @@ public: _Off, _Ptr, _Count); } - basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { // insert [_Ptr, ) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { + // insert [_Ptr, ) at _Off return insert(_Off, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& insert(const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Ch at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; @@ -3293,7 +3410,7 @@ public: _Off, _Count, _Ch); } - iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3302,7 +3419,8 @@ public: return begin() + static_cast(_Off); } - iterator insert(const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER iterator insert( + const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Elem at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3313,7 +3431,7 @@ public: } template , int> = 0> - iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { // insert [_First, _Last) at _Where, input iterators #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3332,14 +3450,14 @@ public: return begin() + static_cast(_Off); } - basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) _Mypair._Myval2._Check_offset(_Off); _Eos(_Off); return *this; } private: - basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { + _CONSTEXPR20_CONTAINER basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); const size_type _Old_size = _Mypair._Myval2._Mysize; _Elem* const _My_ptr = _Mypair._Myval2._Myptr(); @@ -3351,12 +3469,13 @@ private: } public: - basic_string& erase(const size_type _Off, const size_type _Count) { // erase elements [_Off, _Off + _Count) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off, const size_type _Count) { + // erase elements [_Off, _Off + _Count) _Mypair._Myval2._Check_offset(_Off); return _Erase_noexcept(_Off, _Count); } - iterator erase(const const_iterator _Where) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _Where) noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3365,7 +3484,8 @@ public: return begin() + static_cast(_Off); } - iterator erase(const const_iterator _First, const const_iterator _Last) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _First, const const_iterator _Last) noexcept + /* strengthened */ { _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); @@ -3375,17 +3495,18 @@ public: return begin() + static_cast(_Off); } - void clear() noexcept { // erase all + _CONSTEXPR20_CONTAINER void clear() noexcept { // erase all _Eos(0); } - basic_string& replace(const size_type _Off, const size_type _Nx, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const basic_string& _Right) { // replace [_Off, _Off + _Nx) with _Right return replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, const size_type _Roff, - size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, + const size_type _Roff, size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3394,22 +3515,23 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { // replace [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const size_type _Off, size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); @@ -3431,31 +3553,39 @@ public: } const size_type _Growth = static_cast(_Count - _Nx); - if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits - _Mypair._Myval2._Mysize = _Old_size + _Growth; - _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); - _Elem* const _Insert_at = _Old_ptr + _Off; - _Elem* const _Suffix_at = _Insert_at + _Nx; - size_type _Ptr_shifted_after; // see rationale in insert - if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { - _Ptr_shifted_after = _Count; - } else if (_Suffix_at <= _Ptr) { - _Ptr_shifted_after = 0; - } else { - _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); - } + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits + _Mypair._Myval2._Mysize = _Old_size + _Growth; + _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); + _Elem* const _Insert_at = _Old_ptr + _Off; + _Elem* const _Suffix_at = _Insert_at + _Nx; + + size_type _Ptr_shifted_after; // see rationale in insert + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + _Ptr_shifted_after = _Count; + } else if (_Suffix_at <= _Ptr) { + _Ptr_shifted_after = 0; + } else { + _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); + } - _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); - // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; - // this case doesn't occur in insert because the new content must come from outside the removed - // content there (because in insert there is no removed content) - _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); - // the next case can be copy, because it comes from the chunk moved out of the way in the - // first move, and the hole we're filling can't alias the chunk we moved out of the way - _Traits::copy( - _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); - return *this; + _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); + // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; + // this case doesn't occur in insert because the new content must come from outside the removed + // content there (because in insert there is no removed content) + _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); + // the next case can be copy, because it comes from the chunk moved out of the way in the + // first move, and the hole we're filling can't alias the chunk we moved out of the way + _Traits::copy( + _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); + return *this; + } } return _Reallocate_grow_by( @@ -3469,12 +3599,14 @@ public: _Off, _Nx, _Ptr, _Count); } - basic_string& replace(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { // replace [_Off, _Off + _Nx) with [_Ptr, ) return replace(_Off, _Nx, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& replace(const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { // replace [_Off, _Off + _Nx) with _Count * _Ch _Mypair._Myval2._Check_offset(_Off); _Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx); @@ -3484,8 +3616,8 @@ public: } const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count < _Nx - || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { // either we are shrinking, or the growth fits + if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { + // either we are shrinking, or the growth fits _Mypair._Myval2._Mysize = _Old_size + _Count - _Nx; // may temporarily overflow; // OK because size_type must be unsigned _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3506,7 +3638,8 @@ public: _Off, _Nx, _Count, _Ch); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3518,7 +3651,8 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3529,7 +3663,7 @@ public: } #endif // _HAS_CXX17 - basic_string& replace(const const_iterator _First, const const_iterator _Last, + _CONSTEXPR20_CONTAINER basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_First, _Last) with [_Ptr, _Ptr + _Count) _Adl_verify_range(_First, _Last); @@ -3540,7 +3674,8 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr, _Count); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { // replace [_First, _Last) with [_Ptr, ) _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3550,7 +3685,7 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const size_type _Count, const _Elem _Ch) { // replace [_First, _Last) with _Count * _Ch _Adl_verify_range(_First, _Last); @@ -3562,7 +3697,7 @@ public: } template , int> = 0> - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const _Iter _First2, const _Iter _Last2) { // replace [_First, _Last) with [_First2, _Last2), input iterators _Adl_verify_range(_First, _Last); @@ -3582,89 +3717,107 @@ public: } } - _NODISCARD iterator begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator begin() noexcept { return iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator begin() const noexcept { return const_iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD iterator end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator end() noexcept { return iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator end() const noexcept { return const_iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _Elem* _Unchecked_begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_begin() noexcept { return _Mypair._Myval2._Myptr(); } - const _Elem* _Unchecked_begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_begin() const noexcept { return _Mypair._Myval2._Myptr(); } - _Elem* _Unchecked_end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_end() noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - const _Elem* _Unchecked_end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_end() const noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - _NODISCARD reverse_iterator rbegin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - _NODISCARD const_reverse_iterator rbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - _NODISCARD reverse_iterator rend() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - _NODISCARD const_reverse_iterator rend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - _NODISCARD const_iterator cbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cbegin() const noexcept { return begin(); } - _NODISCARD const_iterator cend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cend() const noexcept { return end(); } - _NODISCARD const_reverse_iterator crbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crbegin() const noexcept { return rbegin(); } - _NODISCARD const_reverse_iterator crend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crend() const noexcept { return rend(); } - void shrink_to_fit() { // reduce capacity + _CONSTEXPR20_CONTAINER void shrink_to_fit() { // reduce capacity auto& _My_data = _Mypair._Myval2; - if (!_My_data._Large_string_engaged()) { // can't shrink from small mode - return; - } - if (_My_data._Mysize < _BUF_SIZE) { - _Become_small(); - return; +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (!_My_data._Large_string_engaged()) { // can't shrink from small mode + return; + } + + if (_My_data._Mysize < _BUF_SIZE) { + _Become_small(); + return; + } } - const size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); + size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); +#ifdef __cpp_lib_constexpr_string + // must allocate at least _BUF_SIZE space + _Target_capacity = (_STD max)(_Target_capacity, _BUF_SIZE); +#endif // __cpp_lib_constexpr_string + if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _Target_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Orphan_all(); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_My_data._Bx._Ptr), _My_data._Mysize + 1); _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); @@ -3673,24 +3826,25 @@ public: } } - _NODISCARD reference at(const size_type _Off) { + _NODISCARD _CONSTEXPR20_CONTAINER reference at(const size_type _Off) { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference at(const size_type _Off) const { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference at(const size_type _Off) const { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD reference operator[](const size_type _Off) noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const size_type _Off) noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator[](const size_type _Off) const noexcept + /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3698,13 +3852,13 @@ public: } #if _HAS_CXX17 - /* implicit */ operator basic_string_view<_Elem, _Traits>() const noexcept { + /* implicit */ _CONSTEXPR20_CONTAINER operator basic_string_view<_Elem, _Traits>() const noexcept { // return a string_view around *this's character-type sequence return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize); } #endif // _HAS_CXX17 - void push_back(const _Elem _Ch) { // insert element at end + _CONSTEXPR20_CONTAINER void push_back(const _Elem _Ch) { // insert element at end const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Old_size < _Mypair._Myval2._Myres) { _Mypair._Myval2._Mysize = _Old_size + 1; @@ -3724,7 +3878,7 @@ public: _Ch); } - void pop_back() noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { const size_type _Old_size = _Mypair._Myval2._Mysize; #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Old_size != 0, "invalid to pop_back empty string"); @@ -3732,7 +3886,7 @@ public: _Eos(_Old_size - 1); } - _NODISCARD reference front() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference front() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3740,7 +3894,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD const_reference front() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference front() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3748,7 +3902,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD reference back() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference back() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3756,7 +3910,7 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD const_reference back() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference back() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3764,29 +3918,29 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD _Ret_z_ const _Elem* c_str() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* c_str() const noexcept { return _Mypair._Myval2._Myptr(); } - _NODISCARD _Ret_z_ const _Elem* data() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* data() const noexcept { return _Mypair._Myval2._Myptr(); } #if _HAS_CXX17 - _NODISCARD _Ret_z_ _Elem* data() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ _Elem* data() noexcept { return _Mypair._Myval2._Myptr(); } #endif // _HAS_CXX17 - _NODISCARD size_type length() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type length() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type size() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type max_size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { const size_type _Alloc_max = _Alty_traits::max_size(_Getal()); const size_type _Storage_max = // can always store small string (_STD max)(_Alloc_max, static_cast(_BUF_SIZE)); @@ -3795,7 +3949,7 @@ public: ); } - void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { // determine new length, padding with _Ch elements as needed const size_type _Old_size = size(); if (_Newsize <= _Old_size) { @@ -3805,12 +3959,13 @@ public: } } - _NODISCARD size_type capacity() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { return _Mypair._Myval2._Myres; } #if _HAS_CXX20 - void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { // determine new minimum length of allocated storage + _CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { + // determine new minimum length of allocated storage if (_Mypair._Myval2._Myres >= _Newcap) { // requested capacity is not larger than current capacity, ignore return; // nothing to do } @@ -3860,11 +4015,12 @@ public: } #endif // _HAS_CXX20 - _NODISCARD bool empty() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool empty() const noexcept { return size() == 0; } - size_type copy(_Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER size_type copy( + _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3872,8 +4028,9 @@ public: return _Count; } - _Pre_satisfies_(_Dest_size >= _Count) size_type _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, - const size_type _Dest_size, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER _Pre_satisfies_(_Dest_size >= _Count) size_type + _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, + const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Dest_size) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3881,30 +4038,37 @@ public: return _Count; } - void _Swap_data(basic_string& _Right, true_type) { - // exchange _String_val instances with _Right, memcpy optimization - const auto _My_data_mem = - reinterpret_cast(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset; - const auto _Right_data_mem = - reinterpret_cast(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset; - unsigned char _Temp_mem[_Memcpy_val_size]; - _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); - _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); - _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); - } - void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) { // exchange a string in large mode with one in small mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + const pointer _Ptr = _Starts_large._Bx._Ptr; _Destroy_in_place(_Starts_large._Bx._Ptr); _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } - void _Swap_data(basic_string& _Right, false_type) { - // exchange _String_val instances with _Right, general case - auto& _My_data = _Mypair._Myval2; - auto& _Right_data = _Right._Mypair._Myval2; + _CONSTEXPR20_CONTAINER void _Swap_data(basic_string& _Right) { + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + const auto _My_data_mem = + reinterpret_cast(_STD addressof(_My_data)) + _Memcpy_val_offset; + const auto _Right_data_mem = + reinterpret_cast(_STD addressof(_Right_data)) + _Memcpy_val_offset; + unsigned char _Temp_mem[_Memcpy_val_size]; + _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); + _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); + _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); + return; + } + } + const bool _My_large = _My_data._Large_string_engaged(); const bool _Right_large = _Right_data._Large_string_engaged(); if (_My_large) { @@ -3928,7 +4092,7 @@ public: _STD swap(_My_data._Myres, _Right_data._Myres); } - void swap(basic_string& _Right) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void swap(basic_string& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); @@ -3949,12 +4113,12 @@ public: #endif // _ITERATOR_DEBUG_LEVEL != 0 } - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); } #if _HAS_CXX17 template = 0> - _NODISCARD size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for _Right beginning at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find<_Traits>( @@ -3962,27 +4126,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for _Right beginning at or after _Off return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning at or after _Off return static_cast( _Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning at or after _Off return static_cast(_Traits_find<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -3990,7 +4156,7 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for _Right beginning before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_rfind<_Traits>( @@ -3998,27 +4164,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for _Right beginning before _Off return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning before _Off return static_cast( _Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning before _Off return static_cast(_Traits_rfind<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept + /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4026,7 +4194,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for one of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -4034,28 +4203,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4063,7 +4234,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for one of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -4071,28 +4243,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const basic_string& _Right, size_type _Off = npos) const noexcept { // look for one of _Right before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for one of [_Ptr, ) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4100,7 +4274,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_not_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for none of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4109,29 +4284,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_not_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { // look for non-_Ch at or after _Off return static_cast( _Traits_find_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4139,7 +4315,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_not_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for none of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4148,29 +4325,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_not_of(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for none of _Right before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for none of [_Ptr, ) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for non-_Ch before _Off return static_cast( _Traits_rfind_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4178,34 +4356,40 @@ public: #if _HAS_CXX17 _NODISCARD bool _Starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + // Used exclusively by filesystem return basic_string_view<_Elem, _Traits>(*this)._Starts_with(_Right); } #endif // _HAS_CXX17 - _NODISCARD basic_string substr(const size_type _Off = 0, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER basic_string substr( + const size_type _Off = 0, const size_type _Count = npos) const { // return [_Off, _Off + _Count) as new string return basic_string(*this, _Off, _Count, get_allocator()); } - bool _Equal(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right for equality + _CONSTEXPR20_CONTAINER bool _Equal(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { // compare [0, size()) with _Ptr for equality + _CONSTEXPR20_CONTAINER bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { + // compare [0, size()) with _Ptr for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } #if _HAS_CXX17 template = 0> - _NODISCARD int compare(const _StringViewIsh& _Right) const { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const _StringViewIsh& _Right) const { + // compare [0, size()) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _As_view.data(), _As_view.size()); } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { // compare [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4214,8 +4398,8 @@ public: } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4225,20 +4409,21 @@ public: } #endif // _HAS_CXX17 - _NODISCARD int compare(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { // compare [_Off, _Off + _Nx) with _Right _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, const size_type _Roff, - const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, + const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Mypair._Myval2._Check_offset(_Off); _Right._Mypair._Myval2._Check_offset(_Roff); @@ -4246,57 +4431,59 @@ public: _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count)); } - _NODISCARD int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { // compare [0, size()) with [_Ptr, ) return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { // compare [_Off, _Off + _Nx) with [_Ptr, ) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, - const size_type _Count) const { // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { + // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Count); } #if _HAS_CXX20 - _NODISCARD bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } #endif // _HAS_CXX20 - _NODISCARD allocator_type get_allocator() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } private: - _NODISCARD static size_type _Calculate_growth( + _NODISCARD static _CONSTEXPR20_CONTAINER size_type _Calculate_growth( const size_type _Requested, const size_type _Old, const size_type _Max) noexcept { const size_type _Masked = _Requested | _ALLOC_MASK; if (_Masked > _Max) { // the mask overflows, settle for max_size() @@ -4310,12 +4497,12 @@ private: return (_STD max)(_Masked, _Old + _Old / 2); } - _NODISCARD size_type _Calculate_growth(const size_type _Requested) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type _Calculate_growth(const size_type _Requested) const noexcept { return _Calculate_growth(_Requested, _Mypair._Myval2._Myres, max_size()); } template - basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { // reallocate to store exactly _New_size elements, new buffer prepared by // _Fn(_New_ptr, _New_size, _Args...) if (_New_size > max_size()) { @@ -4326,6 +4513,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Mypair._Myval2._Orphan_all(); _Mypair._Myval2._Mysize = _New_size; _Mypair._Myval2._Myres = _New_capacity; @@ -4341,7 +4534,8 @@ private: } template - basic_string& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_grow_by( + const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { // reallocate to increase size by _Size_increase elements, new buffer prepared by // _Fn(_New_ptr, _Old_ptr, _Old_size, _Args...) auto& _My_data = _Mypair._Myval2; @@ -4355,6 +4549,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _My_data._Orphan_all(); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -4376,6 +4576,8 @@ private: // release any held storage and return to small string mode // pre: *this is in large string mode // pre: this is small enough to return to small string mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + _Mypair._Myval2._Orphan_all(); const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; auto& _Al = _Getal(); @@ -4385,18 +4587,33 @@ private: _Mypair._Myval2._Myres = _BUF_SIZE - 1; } - void _Eos(const size_type _Newsize) { // set new length and null terminator + _CONSTEXPR20_CONTAINER void _Eos(const size_type _Newsize) { // set new length and null terminator _Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _Newsize], _Elem()); } - void _Tidy_init() noexcept { // initialize basic_string data members - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + _CONSTEXPR20_CONTAINER void _Tidy_init() noexcept { // initialize basic_string data members + auto& _My_data = _Mypair._Myval2; + _My_data._Mysize = 0; + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _My_data._Myres = _BUF_SIZE; // SSO disabled in constexpr context + auto& _Al = _Getal(); + const pointer _New_ptr = _Al.allocate(_BUF_SIZE + 1); // throws + _My_data._Bx._Ptr = _New_ptr; + + _Elem* const _Raw_new = _Unfancy(_New_ptr); + _Traits::assign(_Raw_new, _BUF_SIZE + 1, _Elem()); + } else +#endif // __cpp_lib_constexpr_string + { + _My_data._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_My_data._Bx._Buf[0], _Elem()); + } } - void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage + _CONSTEXPR20_CONTAINER void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage _Mypair._Myval2._Orphan_all(); if (_Mypair._Myval2._Large_string_engaged()) { const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; @@ -4405,27 +4622,36 @@ private: _Al.deallocate(_Ptr, _Mypair._Myval2._Myres + 1); } - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _Mypair._Myval2._Bx._Ptr = nullptr; + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = 0; + } else +#endif // __cpp_lib_constexpr_string + { + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + } } public: - void _Orphan_all() noexcept { // used by filesystem::path + _CONSTEXPR20_CONTAINER void _Orphan_all() noexcept { // used by filesystem::path _Mypair._Myval2._Orphan_all(); } private: - void _Swap_proxy_and_iterators(basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators(basic_string& _Right) { _Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2); } - _Alty& _Getal() noexcept { + _CONSTEXPR20_CONTAINER _Alty& _Getal() noexcept { return _Mypair._Get_first(); } - const _Alty& _Getal() const noexcept { + _CONSTEXPR20_CONTAINER const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } @@ -4450,13 +4676,13 @@ basic_string(basic_string_view<_Elem, _Traits>, _Guide_size_type_t<_Alloc>, _Gui #endif // _HAS_CXX17 template -void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept -/* strengthened */ { +_CONSTEXPR20_CONTAINER void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, + basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { _Left.swap(_Right); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Left_size = _Left.size(); const auto _Right_size = _Right.size(); @@ -4468,7 +4694,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Convert_size<_Size_type>(_Traits::length(_Left)); @@ -4481,7 +4707,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Right_size = _Right.size(); if (_Right_size == _Right.max_size()) { @@ -4492,7 +4718,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Left.size(); @@ -4505,7 +4731,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const _Elem _Right) { const auto _Left_size = _Left.size(); if (_Left_size == _Left.max_size()) { @@ -4516,19 +4742,19 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_STD addressof(_Left) != _STD addressof(_Right), @@ -4541,50 +4767,51 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, 1, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, _In_z_ const _Elem* const _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const _Elem _Right) { _Left.push_back(_Right); return _STD move(_Left); } template -_NODISCARD bool operator==( +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Left._Equal(_Right); } template -_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return _Left._Equal(_Right); } #if _HAS_CXX20 template -_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); } template -_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); } @@ -4759,39 +4986,49 @@ basic_ostream<_Elem, _Traits>& operator<<( // basic_string LITERALS inline namespace literals { inline namespace string_literals { - _NODISCARD inline string operator"" s(const char* _Str, size_t _Len) { + +#ifdef __EDG__ // TRANSITION, VSO-1273381 +#define _CONSTEXPR20_STRING_LITERALS inline +#else // ^^^ workaround / no workaround vvv +#define _CONSTEXPR20_STRING_LITERALS _CONSTEXPR20_CONTAINER +#endif // ^^^ no workaround ^^^ + + _NODISCARD _CONSTEXPR20_STRING_LITERALS string operator"" s(const char* _Str, size_t _Len) { return string(_Str, _Len); } - _NODISCARD inline wstring operator"" s(const wchar_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS wstring operator"" s(const wchar_t* _Str, size_t _Len) { return wstring(_Str, _Len); } #ifdef __cpp_char8_t - _NODISCARD inline basic_string operator"" s(const char8_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS basic_string operator"" s(const char8_t* _Str, size_t _Len) { return basic_string(_Str, _Len); } #endif // __cpp_char8_t - _NODISCARD inline u16string operator"" s(const char16_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u16string operator"" s(const char16_t* _Str, size_t _Len) { return u16string(_Str, _Len); } - _NODISCARD inline u32string operator"" s(const char32_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u32string operator"" s(const char32_t* _Str, size_t _Len) { return u32string(_Str, _Len); } + +#undef _CONSTEXPR20_STRING_LITERALS // TRANSITION, VSO-1273381 + } // namespace string_literals } // namespace literals #if _HAS_CXX20 template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( basic_string<_Elem, _Traits, _Alloc>& _Cont, const _Uty& _Val) { return _Erase_remove(_Cont, _Val); } template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( basic_string<_Elem, _Traits, _Alloc>& _Cont, _Pr _Pred) { return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 103230df92f..d687c20516e 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -185,6 +185,7 @@ // P0912R5 Library Support For Coroutines // P0919R3 Heterogeneous Lookup For Unordered Containers // P0966R1 string::reserve() Should Not Shrink +// P0980R1 constexpr std::string // P1001R2 execution::unseq // P1004R2 constexpr std::vector // P1006R1 constexpr For pointer_traits::pointer_to() @@ -1201,10 +1202,15 @@ #define __cpp_lib_constexpr_dynamic_alloc 201907L #endif // __cpp_constexpr_dynamic_alloc -#define __cpp_lib_constexpr_functional 201907L -#define __cpp_lib_constexpr_iterator 201811L -#define __cpp_lib_constexpr_memory 201811L -#define __cpp_lib_constexpr_numeric 201911L +#define __cpp_lib_constexpr_functional 201907L +#define __cpp_lib_constexpr_iterator 201811L +#define __cpp_lib_constexpr_memory 201811L +#define __cpp_lib_constexpr_numeric 201911L + +#if defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) // TRANSITION, LLVM-48606 +#define __cpp_lib_constexpr_string 201907L +#endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) + #define __cpp_lib_constexpr_string_view 201811L #define __cpp_lib_constexpr_tuple 201811L #define __cpp_lib_constexpr_utility 201811L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 6bf998bbc27..7474c58049e 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -358,6 +358,14 @@ std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:0 FAIL std/containers/views/span.cons/ptr_len.pass.cpp:0 FAIL std/containers/views/span.cons/ptr_ptr.pass.cpp:0 FAIL +# DevCom-1336816 "cl errors on constexpr array of std::string" +std/numerics/numeric.ops/adjacent.difference/adjacent_difference_op.pass.cpp:0 FAIL +std/numerics/numeric.ops/partial.sum/partial_sum_op.pass.cpp:0 FAIL + +# DevCom-1336819 "cl constexpr temporary passing issue" +std/numerics/numeric.ops/inner.product/inner_product_comp.pass.cpp:0 FAIL +std/numerics/numeric.ops/accumulate/accumulate_op.pass.cpp:0 FAIL + # VSO-1168723 "Implicit copy assignment incorrectly not constexpr when subobject has explicit constexpr copy constructor" std/utilities/utility/pairs/pairs.pair/assign_pair.pass.cpp:0 FAIL std/utilities/utility/pairs/pairs.pair/assign_rv_pair.pass.cpp:0 FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index c6d0b432698..27ec05b6f26 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -358,6 +358,14 @@ utilities\meta\meta.unary\meta.unary.prop\is_constructible.pass.cpp containers\views\span.cons\ptr_len.pass.cpp containers\views\span.cons\ptr_ptr.pass.cpp +# DevCom-1336816 "cl errors on constexpr array of std::string" +numerics\numeric.ops\adjacent.difference\adjacent_difference_op.pass.cpp +numerics\numeric.ops\partial.sum\partial_sum_op.pass.cpp + +# DevCom-1336819 "cl constexpr temporary passing issue" +numerics\numeric.ops\inner.product\inner_product_comp.pass.cpp +numerics\numeric.ops\accumulate\accumulate_op.pass.cpp + # VSO-1168723 "Implicit copy assignment incorrectly not constexpr when subobject has explicit constexpr copy constructor" utilities\utility\pairs\pairs.pair\assign_pair.pass.cpp utilities\utility\pairs\pairs.pair\assign_rv_pair.pass.cpp diff --git a/tests/std/include/test_death.hpp b/tests/std/include/test_death.hpp index 943b0f28a02..296a0f69012 100644 --- a/tests/std/include/test_death.hpp +++ b/tests/std/include/test_death.hpp @@ -79,9 +79,16 @@ namespace std_testing { // buffer was not big enough const size_t str_max_size = result.max_size(); const size_t result_max_size = str_max_size - str_max_size / 2; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif // __clang__ if (result_size >= result_max_size) { api_unexpected("GetModuleFileNameW"); } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ result.resize(result_size + result_size / 2); } else if (result_size == 0) { diff --git a/tests/std/test.lst b/tests/std/test.lst index 4e57dedd49d..cd16f4d0e15 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -387,6 +387,7 @@ tests\P0898R3_identity tests\P0912R5_coroutine tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink +tests\P0980R1_constexpr_strings tests\P1004R2_constexpr_vector tests\P1004R2_constexpr_vector_bool tests\P1007R3_assume_aligned diff --git a/tests/std/tests/P0218R1_filesystem/test.cpp b/tests/std/tests/P0218R1_filesystem/test.cpp index 610e1003b8b..aea07892627 100644 --- a/tests/std/tests/P0218R1_filesystem/test.cpp +++ b/tests/std/tests/P0218R1_filesystem/test.cpp @@ -3908,7 +3908,9 @@ void run_interactive_tests(int argc, wchar_t* argv[]) { // mode. void test_devcom_953628() { // COMPILE-ONLY struct S : wstring {}; +#if defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1270432 path{S{}}; +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) } int wmain(int argc, wchar_t* argv[]) { diff --git a/tests/std/tests/P0980R1_constexpr_strings/env.lst b/tests/std/tests/P0980R1_constexpr_strings/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0980R1_constexpr_strings/test.cpp b/tests/std/tests/P0980R1_constexpr_strings/test.cpp new file mode 100644 index 00000000000..81044226f02 --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/test.cpp @@ -0,0 +1,1825 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr auto literal_input = "Hello fluffy kittens"; +constexpr auto literal_input_u16 = u"Hello fluffy kittens"; +constexpr auto literal_input_u32 = U"Hello fluffy kittens"; +constexpr auto literal_input_w = L"Hello fluffy kittens"; + +constexpr auto view_input = "Hello fluffy kittens"sv; +constexpr auto view_input_u16 = u"Hello fluffy kittens"sv; +constexpr auto view_input_u32 = U"Hello fluffy kittens"sv; +constexpr auto view_input_w = L"Hello fluffy kittens"sv; + +#ifdef __cpp_char8_t +constexpr auto literal_input_u8 = u8"Hello fluffy kittens"; +constexpr auto view_input_u8 = u8"Hello fluffy kittens"sv; +#endif // __cpp_char8_t + +template +constexpr auto get_literal_input() { + if constexpr (is_same_v) { + return literal_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u16; + } else if constexpr (is_same_v) { + return literal_input_u32; + } else { + return literal_input_w; + } +} + +template +constexpr auto get_view_input() { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } +} + +template +constexpr auto get_cat() { + if constexpr (is_same_v) { + return "kitten"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"kitten"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"kitten"; + } else if constexpr (is_same_v) { + return U"kitten"; + } else { + return L"kitten"; + } +} + +template +constexpr auto get_dog() { + if constexpr (is_same_v) { + return "dog"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"dog"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"dog"; + } else if constexpr (is_same_v) { + return U"dog"; + } else { + return L"dog"; + } +} + +template +constexpr auto get_no_needle() { + if constexpr (is_same_v) { + return "vxz"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"vxz"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"vxz"; + } else if constexpr (is_same_v) { + return U"vxz"; + } else { + return L"vxz"; + } +} + +template +constexpr auto get_cute_and_scratchy() { + if constexpr (is_same_v) { + return "cute and scratchy "; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"cute and scratchy "; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"cute and scratchy "; + } else if constexpr (is_same_v) { + return U"cute and scratchy "; + } else { + return L"cute and scratchy "; + } +} + +template +struct string_view_convertible { + _CONSTEXPR20_CONTAINER operator basic_string_view() const { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } + } +}; + +// TRANSITION, EDG concepts support +template +constexpr bool equalRanges(const Range1& range1, const Range2& range2) noexcept { +#ifdef __cpp_lib_concepts + return ranges::equal(range1, range2); +#else // ^^^ __cpp_lib_concepts ^^^ / vvv !__cpp_lib_concepts vvv + return equal(begin(range1), end(range1), begin(range2), end(range2)); +#endif // !__cpp_lib_concepts +} + +template +_CONSTEXPR20_CONTAINER bool test_interface() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 + { // constructors + // range constructors + str literal_constructed{get_literal_input()}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed(get_view_input()); + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed; + assert(default_constructed.empty()); + + str copy_constructed(literal_constructed); + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed(move(copy_constructed)); + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + str copy_assigned(get_dog()); + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned(get_dog()); + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end()); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // allocator constructors + allocator alloc; + + // range constructors + str literal_constructed{get_literal_input(), alloc}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed{get_view_input(), alloc}; + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}, alloc); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed{alloc}; + assert(default_constructed.empty()); + + str copy_constructed{literal_constructed, alloc}; + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed{move(copy_constructed), alloc}; + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}, alloc); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2, alloc); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3, alloc); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2, alloc); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end(), alloc); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible, alloc); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3, alloc); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // assignment operator + str literal_constructed = get_literal_input(); + + str copy_assigned; + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned; + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + str literal_assigned; + literal_assigned = get_literal_input(); + assert(equalRanges(literal_assigned, literal_constructed)); + + str char_assigned; + char_assigned = CharType{'!'}; + assert(equalRanges(char_assigned, "!"sv)); + + str initializer_list_assigned; + initializer_list_assigned = {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(initializer_list_assigned, "meow"sv)); + + const string_view_convertible convertible; + str conversion_assigned; + conversion_assigned = convertible; + assert(equalRanges(conversion_assigned, literal_constructed)); + } + + { // assign + str literal_constructed = get_literal_input(); + + str assign_size_char; + assign_size_char.assign(5, CharType{'a'}); + assert(equalRanges(assign_size_char, "aaaaa"sv)); + + str assign_str; + assign_str.assign(literal_constructed); + assert(equalRanges(assign_str, literal_constructed)); + + str assign_str_pos; + assign_str_pos.assign(literal_constructed, 2); + assert(equalRanges(assign_str_pos, "llo fluffy kittens"sv)); + + str assign_str_pos_len; + assign_str_pos_len.assign(literal_constructed, 2, 3); + assert(equalRanges(assign_str_pos_len, "llo"sv)); + + str assign_moved_str; + assign_moved_str.assign(move(assign_str_pos_len)); + assert(equalRanges(assign_moved_str, "llo"sv)); + assert(assign_str_pos_len.empty()); + + str assign_literal; + assign_literal.assign(get_literal_input()); + assert(equalRanges(assign_literal, literal_constructed)); + + str assign_literal_count; + assign_literal_count.assign(get_literal_input(), 2); + assert(equalRanges(assign_literal_count, "He"sv)); + + str assign_iterator; + assign_iterator.assign(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(assign_iterator, get_view_input())); + + str assign_initializer_list; + assign_initializer_list.assign({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(assign_initializer_list, "meow"sv)); + + const string_view_convertible convertible; + str assign_conversion; + assign_conversion.assign(convertible); + assert(equalRanges(assign_conversion, literal_constructed)); + + str assign_conversion_start_length; + assign_conversion_start_length.assign(convertible, 2, 3); + assert(equalRanges(assign_conversion_start_length, "llo"sv)); + } +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 + { // allocator + str default_constructed; + [[maybe_unused]] const auto alloc = default_constructed.get_allocator(); + static_assert(is_same_v, allocator>); + } +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 + { // access + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto at = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at == CharType{'l'}); + + literal_constructed.at(2) = CharType{'v'}; + const auto at2 = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at2 == CharType{'v'}); + + const auto cat = const_literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(cat == CharType{'l'}); + + const auto op = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op == CharType{'l'}); + + literal_constructed[3] = CharType{'u'}; + const auto op2 = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op2 == CharType{'u'}); + + const auto cop = const_literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(cop == CharType{'l'}); + + const auto f = literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(f == CharType{'H'}); + + const auto cf = const_literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(cf == CharType{'H'}); + + const auto b = literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(b == CharType{'s'}); + + const auto cb = const_literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(cb == CharType{'s'}); + + const auto d = literal_constructed.data(); + static_assert(is_same_v); + assert(*d == CharType{'H'}); + + const auto cd = const_literal_constructed.data(); + static_assert(is_same_v); + assert(*cd == CharType{'H'}); + + const auto cs = literal_constructed.c_str(); + static_assert(is_same_v); + assert(cs == literal_constructed.data()); + assert(char_traits::length(cs) == literal_constructed.size()); + } + + { // iterators + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto b = literal_constructed.begin(); + static_assert(is_same_v, typename str::iterator>); + assert(*b == CharType{'H'}); + + const auto cb = literal_constructed.cbegin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb == CharType{'H'}); + + const auto cb2 = const_literal_constructed.begin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb2 == CharType{'H'}); + + const auto e = literal_constructed.end(); + static_assert(is_same_v, typename str::iterator>); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + assert(*prev(e) == CharType{'s'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + + const auto ce = literal_constructed.cend(); + static_assert(is_same_v, typename str::const_iterator>); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + assert(*prev(ce) == CharType{'s'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + + const auto ce2 = const_literal_constructed.end(); + static_assert(is_same_v, typename str::const_iterator>); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + assert(*prev(ce2) == CharType{'s'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, 16.10p1 + const auto rb = literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*rb == CharType{'s'}); + + const auto crb = literal_constructed.crbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb == CharType{'s'}); + + const auto crb2 = const_literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb2 == CharType{'s'}); + + const auto re = literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(re) == CharType{'H'}); + + const auto cre = literal_constructed.crend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre) == CharType{'H'}); + + const auto cre2 = const_literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre2) == CharType{'H'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 + } + + { // capacity + str literal_constructed = get_literal_input(); + + const auto e = literal_constructed.empty(); + static_assert(is_same_v, bool>); + assert(!e); + + const auto s = literal_constructed.size(); + static_assert(is_same_v, size_t>); + assert(s == size(get_view_input())); + + const auto l = literal_constructed.length(); + static_assert(is_same_v, size_t>); + assert(l == s); + + const auto ms = literal_constructed.max_size(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(ms == static_cast(-1) / sizeof(CharType) - 1); + } else { + assert(ms == static_cast(-1) / 2); + } + + literal_constructed.reserve(20); + + const auto c = literal_constructed.capacity(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c == 23); + } else { + assert(c == 31); + } + + // make reserve actually do work + literal_constructed.reserve(35); + const auto c2 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v) { + assert(c2 == 39); + } else if constexpr (is_same_v) { + assert(c2 == 35); + } else { + assert(c2 == 47); + } + + // shrink back to previous size + literal_constructed.shrink_to_fit(); + + const auto c3 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c3 == 23); + } else { + assert(c3 == 31); + } + + literal_constructed.erase(3); + literal_constructed.shrink_to_fit(); + + const auto c4 = literal_constructed.capacity(); + if (is_constant_evaluated()) { // check minimum allocation of _BUF_SIZE when constant evaluated + assert(c4 == 16 / sizeof(CharType)); + } else { + if constexpr (is_same_v || is_same_v) { + assert(c4 == 7); + } else if constexpr (is_same_v) { + assert(c4 == 3); + } else { + assert(c4 == 15); + } + } + } + + { // clear + str cleared = get_literal_input(); + cleared.clear(); + assert(cleared.empty()); + assert(cleared.capacity() == str{get_literal_input()}.capacity()); + } + + { // insert +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str insert_char = get_literal_input(); + const CharType to_be_inserted = CharType{','}; + insert_char.insert(insert_char.begin() + 5, to_be_inserted); + assert(equalRanges(insert_char, "Hello, fluffy kittens"sv)); + + str insert_const_char = get_literal_input(); + insert_const_char.insert(insert_const_char.cbegin() + 5, to_be_inserted); + assert(equalRanges(insert_const_char, "Hello, fluffy kittens"sv)); + + str insert_char_rvalue = get_literal_input(); + insert_char_rvalue.insert(insert_char_rvalue.begin() + 5, CharType{','}); + assert(equalRanges(insert_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_const_char_rvalue = get_literal_input(); + insert_const_char_rvalue.insert(insert_const_char_rvalue.cbegin() + 5, CharType{','}); + assert(equalRanges(insert_const_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_range(2, CharType{'b'}); + const auto it = insert_range.insert( + insert_range.begin() + 1, begin(get_view_input()), end(get_view_input())); + assert(it == insert_range.begin() + 1); + assert(equalRanges(insert_range, "bHello fluffy kittensb"sv)); + + str insert_const_range(2, CharType{'b'}); + const auto cit = insert_const_range.insert( + insert_const_range.cbegin() + 1, begin(get_view_input()), end(get_view_input())); + assert(cit == insert_const_range.cbegin() + 1); + assert(equalRanges(insert_const_range, "bHello fluffy kittensb"sv)); + + str insert_initializer_list = get_literal_input(); + const auto it_ilist = insert_initializer_list.insert(insert_initializer_list.begin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(it_ilist == insert_initializer_list.begin() + 6); + assert(equalRanges(insert_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_const_initializer_list = get_literal_input(); + const auto cit_ilist = insert_const_initializer_list.insert(insert_const_initializer_list.cbegin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(cit_ilist == insert_const_initializer_list.cbegin() + 6); + assert(equalRanges(insert_const_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_pos_str = get_literal_input(); + const str to_insert = get_cute_and_scratchy(); + insert_pos_str.insert(6, to_insert); + assert(equalRanges(insert_pos_str, "Hello cute and scratchy fluffy kittens"sv)); + + str insert_pos_substr = get_literal_input(); + insert_pos_substr.insert(6, to_insert, 0, 5); + assert(equalRanges(insert_pos_substr, "Hello cute fluffy kittens"sv)); + + const string_view_convertible convertible; + str insert_pos_conversion = get_literal_input(); + insert_pos_conversion.insert(6, convertible); + assert(equalRanges(insert_pos_conversion, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_conversion_substr = get_literal_input(); + insert_pos_conversion_substr.insert(6, convertible, 6, 7); + assert(equalRanges(insert_pos_conversion_substr, "Hello fluffy fluffy kittens"sv)); + + str insert_pos_literal = get_literal_input(); + insert_pos_literal.insert(6, get_literal_input()); + assert(equalRanges(insert_pos_literal, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_literal_substr = get_literal_input(); + insert_pos_literal_substr.insert(6, get_literal_input(), 6); + assert(equalRanges(insert_pos_literal_substr, "Hello Hello fluffy kittens"sv)); + + str insert_pos_count_char = get_literal_input(); + insert_pos_count_char.insert(6, 3, CharType{'b'}); + assert(equalRanges(insert_pos_count_char, "Hello bbbfluffy kittens"sv)); + + str insert_iter_count_char = get_literal_input(); + insert_iter_count_char.insert(begin(insert_iter_count_char) + 5, 4, CharType{'o'}); + assert(equalRanges(insert_iter_count_char, "Hellooooo fluffy kittens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + } + + { // erase + str erase_pos_count = get_literal_input(); + erase_pos_count.erase(0, 6); + assert(equalRanges(erase_pos_count, "fluffy kittens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str erase_iter = get_literal_input(); + erase_iter.erase(erase_iter.begin()); + assert(equalRanges(erase_iter, "ello fluffy kittens"sv)); + + str erase_const_iter = get_literal_input(); + erase_const_iter.erase(erase_const_iter.cbegin()); + assert(equalRanges(erase_const_iter, "ello fluffy kittens"sv)); + + str erase_iter_iter = get_literal_input(); + erase_iter_iter.erase(erase_iter_iter.begin(), erase_iter_iter.begin() + 6); + assert(equalRanges(erase_iter_iter, "fluffy kittens"sv)); + + str erase_const_iter_iter = get_literal_input(); + erase_const_iter_iter.erase(erase_const_iter_iter.cbegin(), erase_const_iter_iter.cbegin() + 6); + assert(equalRanges(erase_const_iter_iter, "fluffy kittens"sv)); + + str erased_free = get_literal_input(); + erase(erased_free, CharType{'l'}); + assert(equalRanges(erased_free, "Heo fuffy kittens"sv)); + + str erased_free_if = get_literal_input(); + erase_if(erased_free_if, [](const CharType val) { return val == CharType{'t'}; }); + assert(equalRanges(erased_free_if, "Hello fluffy kiens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + } +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 + + { // push_back / pop_back + str pushed; + pushed.push_back(CharType{'y'}); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + + const CharType to_be_pushed = CharType{'z'}; + pushed.push_back(to_be_pushed); + assert(pushed.size() == 2); + assert(pushed.back() == CharType{'z'}); + + pushed.pop_back(); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + } +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 + { // append + const str literal_constructed = get_literal_input(); + + str append_size_char(2, CharType{'b'}); + append_size_char.append(5, CharType{'a'}); + assert(equalRanges(append_size_char, "bbaaaaa"sv)); + + str append_str(2, CharType{'b'}); + append_str.append(literal_constructed); + assert(equalRanges(append_str, "bbHello fluffy kittens"sv)); + + str append_str_pos(2, CharType{'b'}); + append_str_pos.append(literal_constructed, 2); + assert(equalRanges(append_str_pos, "bbllo fluffy kittens"sv)); + + str append_str_pos_len(2, CharType{'b'}); + append_str_pos_len.append(literal_constructed, 2, 3); + assert(equalRanges(append_str_pos_len, "bbllo"sv)); + + str append_literal(2, CharType{'b'}); + append_literal.append(get_literal_input()); + assert(equalRanges(append_literal, "bbHello fluffy kittens"sv)); + + str append_literal_count(2, CharType{'b'}); + append_literal_count.append(get_literal_input(), 2); + assert(equalRanges(append_literal_count, "bbHe"sv)); + + str append_iterator(2, CharType{'b'}); + append_iterator.append(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(append_iterator, "bbHello fluffy kittens"sv)); + + str append_initializer_list(2, CharType{'b'}); + append_initializer_list.append({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(append_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str append_conversion(2, CharType{'b'}); + append_conversion.append(convertible); + assert(equalRanges(append_conversion, "bbHello fluffy kittens"sv)); + + str append_conversion_start_length(2, CharType{'b'}); + append_conversion_start_length.append(convertible, 2, 3); + assert(equalRanges(append_conversion_start_length, "bbllo"sv)); + } + + { // operator+= + str literal_constructed = get_literal_input(); + + str plus_string(2, CharType{'b'}); + plus_string += literal_constructed; + assert(equalRanges(plus_string, "bbHello fluffy kittens"sv)); + + str plus_character(2, CharType{'b'}); + plus_character += CharType{'a'}; + assert(equalRanges(plus_character, "bba"sv)); + + str plus_literal(2, CharType{'b'}); + plus_literal += get_literal_input(); + assert(equalRanges(plus_literal, "bbHello fluffy kittens"sv)); + + str plus_initializer_list(2, CharType{'b'}); + plus_initializer_list += {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(plus_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str plus_conversion(2, CharType{'b'}); + plus_conversion += convertible; + assert(equalRanges(plus_conversion, "bbHello fluffy kittens"sv)); + } + + { // compare + const str first = get_literal_input(); + const str second = get_cat(); + + const int comp_str_eq = first.compare(first); + assert(comp_str_eq == 0); + + const int comp_str_less = first.compare(second); + assert(comp_str_less == -1); + + const int comp_str_greater = second.compare(first); + assert(comp_str_greater == 1); + + const int comp_pos_count_str_eq = first.compare(3, 7, first.substr(3, 7)); + assert(comp_pos_count_str_eq == 0); + + const int comp_pos_count_str_less = first.compare(0, 2, second); + assert(comp_pos_count_str_less == -1); + + const int comp_pos_count_str_greater = second.compare(0, 2, first); + assert(comp_pos_count_str_greater == 1); + + const int comp_pos_count_str_pos_eq = first.compare(3, 20, first, 3); + assert(comp_pos_count_str_pos_eq == 0); + + const int comp_pos_count_str_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_str_pos_less == -1); + + const int comp_pos_count_str_pos_greater = second.compare(0, 2, first, 6); + assert(comp_pos_count_str_pos_greater == 1); + + const int comp_pos_count_str_pos_count_eq = first.compare(3, 5, first, 3, 5); + assert(comp_pos_count_str_pos_count_eq == 0); + + const int comp_pos_count_str_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_str_pos_count_less == -1); + + const int comp_pos_count_str_pos_count_greater = second.compare(0, 2, first, 6, 4); + assert(comp_pos_count_str_pos_count_greater == 1); + + const int comp_literal_eq = first.compare(get_literal_input()); + assert(comp_literal_eq == 0); + + const int comp_literal_less = first.compare(get_cat()); + assert(comp_literal_less == -1); + + const int comp_literal_greater = second.compare(get_literal_input()); + assert(comp_literal_greater == 1); + + const int comp_pos_count_literal_eq = first.compare(13, 6, get_cat()); + assert(comp_pos_count_literal_eq == 0); + + const int comp_pos_count_literal_less = first.compare(0, 2, get_cat()); + assert(comp_pos_count_literal_less == -1); + + const int comp_pos_count_literal_greater = second.compare(0, 2, get_literal_input()); + assert(comp_pos_count_literal_greater == 1); + + const int comp_pos_count_literal_count_eq = first.compare(13, 5, get_cat(), 5); + assert(comp_pos_count_literal_count_eq == 0); + + const int comp_pos_count_literal_count_less = first.compare(0, 2, get_cat(), 2); + assert(comp_pos_count_literal_count_less == -1); + + const int comp_pos_count_literal_count_greater = second.compare(0, 2, get_literal_input(), 6); + assert(comp_pos_count_literal_count_greater == 1); + + const int comp_pos_count_literal_pos_count_eq = first.compare(3, 5, get_literal_input(), 3, 5); + assert(comp_pos_count_literal_pos_count_eq == 0); + + const int comp_pos_count_literal_pos_count_less = first.compare(0, 2, get_cat(), 2, 3); + assert(comp_pos_count_literal_pos_count_less == -1); + + const int comp_pos_count_literal_pos_count_greater = second.compare(0, 2, get_literal_input(), 6, 4); + assert(comp_pos_count_literal_pos_count_greater == 1); + + const string_view_convertible convertible; + const int comp_conversion_eq = first.compare(convertible); + assert(comp_conversion_eq == 0); + + const int comp_conversion_less = first.compare(second); + assert(comp_conversion_less == -1); + + const int comp_conversion_greater = second.compare(convertible); + assert(comp_conversion_greater == 1); + + const int comp_pos_count_conversion_eq = first.compare(0, 20, convertible); + assert(comp_pos_count_conversion_eq == 0); + + const int comp_pos_count_conversion_less = first.compare(5, 4, convertible); + assert(comp_pos_count_conversion_less == -1); + + const int comp_pos_count_conversion_greater = second.compare(0, 2, convertible); + assert(comp_pos_count_conversion_greater == 1); + + const int comp_pos_count_conversion_pos_eq = first.compare(3, 20, convertible, 3); + assert(comp_pos_count_conversion_pos_eq == 0); + + const int comp_pos_count_conversion_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_conversion_pos_less == -1); + + const int comp_pos_count_conversion_pos_greater = second.compare(0, 2, convertible, 6); + assert(comp_pos_count_conversion_pos_greater == 1); + + const int comp_pos_count_conversion_pos_count_eq = first.compare(3, 5, convertible, 3, 5); + assert(comp_pos_count_conversion_pos_count_eq == 0); + + const int comp_pos_count_conversion_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_conversion_pos_count_less == -1); + + const int comp_pos_count_conversion_pos_count_greater = second.compare(0, 2, convertible, 6, 4); + assert(comp_pos_count_conversion_pos_count_greater == 1); + } + + { // starts_with + const str starts = get_literal_input(); + const str input_string_true = starts.substr(0, 5); + assert(starts.starts_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!starts.starts_with(input_string_false)); + + assert(starts.starts_with(CharType{'H'})); + assert(!starts.starts_with(CharType{'h'})); + + assert(starts.starts_with(get_literal_input())); + assert(!input_string_false.starts_with(get_literal_input())); + } + + { // ends_with + const str ends = get_literal_input(); + const str input_string_true = ends.substr(5); + assert(ends.ends_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!ends.ends_with(input_string_false)); + + assert(ends.ends_with(CharType{'s'})); + assert(!ends.ends_with(CharType{'S'})); + + assert(ends.ends_with(get_literal_input())); + assert(!input_string_false.ends_with(get_literal_input())); + } + + { // replace + const str input = get_dog(); + + str replaced_pos_count_str = get_literal_input(); + replaced_pos_count_str.replace(13, 7, input); + assert(equalRanges(replaced_pos_count_str, "Hello fluffy dog"sv)); + + str replaced_pos_count_str_shift = get_literal_input(); + replaced_pos_count_str_shift.replace(13, 2, input); + assert(equalRanges(replaced_pos_count_str_shift, "Hello fluffy dogttens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str replaced_iter_str = get_literal_input(); + replaced_iter_str.replace(replaced_iter_str.cbegin() + 13, replaced_iter_str.cend(), input); + assert(equalRanges(replaced_iter_str, "Hello fluffy dog"sv)); + + str replaced_iter_str_shift = get_literal_input(); + replaced_iter_str_shift.replace( + replaced_iter_str_shift.cbegin() + 13, replaced_iter_str_shift.cbegin() + 15, input); + assert(equalRanges(replaced_iter_str_shift, "Hello fluffy dogttens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + str replaced_pos_count_str_pos_count = get_literal_input(); + replaced_pos_count_str_pos_count.replace(13, 7, input, 1); + assert(equalRanges(replaced_pos_count_str_pos_count, "Hello fluffy og"sv)); + + str replaced_pos_count_str_pos_count_less = get_literal_input(); + replaced_pos_count_str_pos_count_less.replace(13, 2, input, 1, 2); + assert(equalRanges(replaced_pos_count_str_pos_count_less, "Hello fluffy ogttens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str replaced_iter_iter = get_literal_input(); + replaced_iter_iter.replace( + replaced_iter_iter.cbegin() + 13, replaced_iter_iter.cend(), input.begin(), input.end()); + assert(equalRanges(replaced_iter_iter, "Hello fluffy dog"sv)); + + str replaced_iter_iter_less = get_literal_input(); + replaced_iter_iter_less.replace(replaced_iter_iter_less.cbegin() + 13, replaced_iter_iter_less.cbegin() + 15, + input.begin() + 1, input.end()); + assert(equalRanges(replaced_iter_iter_less, "Hello fluffy ogttens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + str replaced_pos_count_literal = get_literal_input(); + replaced_pos_count_literal.replace(13, 2, get_dog()); + assert(equalRanges(replaced_pos_count_literal, "Hello fluffy dogttens"sv)); + + str replaced_pos_count_literal_count = get_literal_input(); + replaced_pos_count_literal_count.replace(13, 2, get_dog(), 2); + assert(equalRanges(replaced_pos_count_literal_count, "Hello fluffy dottens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str replaced_iter_literal = get_literal_input(); + replaced_iter_literal.replace( + replaced_iter_literal.cbegin() + 13, replaced_iter_literal.cbegin() + 15, get_dog()); + assert(equalRanges(replaced_iter_literal, "Hello fluffy dogttens"sv)); + + str replaced_iter_literal_count = get_literal_input(); + replaced_iter_literal_count.replace(replaced_iter_literal_count.cbegin() + 13, + replaced_iter_literal_count.cbegin() + 15, get_dog(), 2); + assert(equalRanges(replaced_iter_literal_count, "Hello fluffy dottens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + str replaced_pos_count_chars = get_literal_input(); + replaced_pos_count_chars.replace(13, 2, 5, CharType{'a'}); + assert(equalRanges(replaced_pos_count_chars, "Hello fluffy aaaaattens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str replaced_iter_chars = get_literal_input(); + replaced_iter_chars.replace( + replaced_iter_chars.cbegin() + 13, replaced_iter_chars.cbegin() + 15, 5, CharType{'a'}); + assert(equalRanges(replaced_iter_chars, "Hello fluffy aaaaattens"sv)); + + str replaced_iter_init = get_literal_input(); + replaced_iter_init.replace(replaced_iter_init.cbegin() + 13, replaced_iter_init.cbegin() + 15, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(equalRanges(replaced_iter_init, "Hello fluffy cute ttens"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str replaced_pos_count_conversion = get_dog(); + replaced_pos_count_conversion.replace(1, 5, convertible); + assert(equalRanges(replaced_pos_count_conversion, "dHello fluffy kittens"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + str replaced_iter_conversion = get_dog(); + replaced_iter_conversion.replace( + replaced_iter_conversion.cbegin() + 1, replaced_iter_conversion.cbegin() + 2, convertible); + assert(equalRanges(replaced_iter_conversion, "dHello fluffy kittensg"sv)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + str replaced_pos_count_conversion_pos = get_dog(); + replaced_pos_count_conversion_pos.replace(1, 5, convertible, 6); + assert(equalRanges(replaced_pos_count_conversion_pos, "dfluffy kittens"sv)); + + str replaced_pos_count_conversion_pos_count = get_dog(); + replaced_pos_count_conversion_pos_count.replace(1, 5, convertible, 6, 6); + assert(equalRanges(replaced_pos_count_conversion_pos_count, "dfluffy"sv)); + } + + { // substr + const str input = get_literal_input(); + + const str substr_pos = input.substr(6); + assert(equalRanges(substr_pos, "fluffy kittens"sv)); + + const str substr_pos_count = input.substr(6, 6); + assert(equalRanges(substr_pos_count, "fluffy"sv)); + } + + { // copy + const str input = get_literal_input(); + + CharType copy_count[5]; + input.copy(copy_count, 5); + assert(equalRanges(copy_count, "Hello"sv)); + + CharType copy_count_pos[6]; + input.copy(copy_count_pos, 6, 6); + assert(equalRanges(copy_count_pos, "fluffy"sv)); + } + + { // resize + str resized = get_literal_input(); + resized.resize(3); + assert(equalRanges(resized, "Hel"sv)); + + resized.resize(6, CharType{'a'}); + assert(equalRanges(resized, "Helaaa"sv)); + } + + { // swap + constexpr basic_string_view expected_first = get_dog(); + constexpr basic_string_view expected_second = get_cat(); + str first{get_cat()}; + str second{get_dog()}; + swap(first, second); + + assert(equalRanges(first, expected_first)); + assert(equalRanges(second, expected_second)); + + first.swap(second); + assert(equalRanges(second, expected_first)); + assert(equalRanges(first, expected_second)); + } + + { // find + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_str = input.find(needle); + assert(find_str == 13u); + + const auto find_str_none = input.find(no_needle); + assert(find_str_none == str::npos); + + const auto find_str_pos = input.find(needle, 6); + assert(find_str_pos == 13u); + + const auto find_str_pos_none = input.find(needle, 14); + assert(find_str_pos_none == str::npos); + + const auto find_str_overflow = input.find(needle, 50); + assert(find_str_overflow == str::npos); + + const auto find_literal = input.find(get_cat()); + assert(find_literal == 13u); + + const auto find_literal_none = input.find(get_dog()); + assert(find_literal_none == str::npos); + + const auto find_literal_pos = input.find(get_cat(), 6); + assert(find_literal_pos == 13u); + + const auto find_literal_pos_none = input.find(get_cat(), 14); + assert(find_literal_pos_none == str::npos); + + const auto find_literal_overflow = input.find(get_cat(), 50); + assert(find_literal_overflow == str::npos); + + const auto find_literal_pos_count = input.find(get_cat(), 6, 4); + assert(find_literal_pos_count == 13u); + + const auto find_literal_pos_count_none = input.find(get_dog(), 14, 4); + assert(find_literal_pos_count_none == str::npos); + + const auto find_char = input.find(CharType{'e'}); + assert(find_char == 1u); + + const auto find_char_none = input.find(CharType{'x'}); + assert(find_char_none == str::npos); + + const auto find_char_pos = input.find(CharType{'e'}, 4); + assert(find_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_convertible = input.find(convertible); + assert(find_convertible == 0); + + const auto find_convertible_pos = input.find(convertible, 2); + assert(find_convertible_pos == str::npos); + } + + { // rfind + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto rfind_str = input.rfind(needle); + assert(rfind_str == 13u); + + const auto rfind_str_none = input.rfind(no_needle); + assert(rfind_str_none == str::npos); + + const auto rfind_str_pos = input.rfind(needle, 15); + assert(rfind_str_pos == 13u); + + const auto rfind_str_pos_none = input.rfind(needle, 6); + assert(rfind_str_pos_none == str::npos); + + const auto rfind_str_overflow = input.rfind(needle, 50); + assert(rfind_str_overflow == 13u); + + const auto rfind_literal = input.rfind(get_cat()); + assert(rfind_literal == 13u); + + const auto rfind_literal_none = input.rfind(get_dog()); + assert(rfind_literal_none == str::npos); + + const auto rfind_literal_pos = input.rfind(get_cat(), 15); + assert(rfind_literal_pos == 13u); + + const auto rfind_literal_pos_none = input.rfind(get_cat(), 6); + assert(rfind_literal_pos_none == str::npos); + + const auto rfind_literal_overflow = input.rfind(get_cat(), 50); + assert(rfind_literal_overflow == 13u); + + const auto rfind_literal_pos_count = input.rfind(get_cat(), 15, 4); + assert(rfind_literal_pos_count == 13u); + + const auto rfind_literal_pos_count_none = input.rfind(get_dog(), 6, 4); + assert(rfind_literal_pos_count_none == str::npos); + + const auto rfind_char = input.rfind(CharType{'e'}); + assert(rfind_char == 17u); + + const auto rfind_char_none = input.rfind(CharType{'x'}); + assert(rfind_char_none == str::npos); + + const auto rfind_char_pos = input.rfind(CharType{'e'}, 4); + assert(rfind_char_pos == 1u); + + const string_view_convertible convertible; + const auto rfind_convertible = input.rfind(convertible); + assert(rfind_convertible == 0); + + const auto rfind_convertible_pos = input.rfind(convertible, 5); + assert(rfind_convertible_pos == 0); + } + + { // find_first_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_of_str = input.find_first_of(needle); + assert(find_first_of_str == 1u); + + const auto find_first_of_str_none = input.find_first_of(no_needle); + assert(find_first_of_str_none == str::npos); + + const auto find_first_of_str_pos = input.find_first_of(needle, 6); + assert(find_first_of_str_pos == 13u); + + const auto find_first_of_str_pos_none = input.find_first_of(no_needle, 14); + assert(find_first_of_str_pos_none == str::npos); + + const auto find_first_of_str_overflow = input.find_first_of(needle, 50); + assert(find_first_of_str_overflow == str::npos); + + const auto find_first_of_literal = input.find_first_of(get_cat()); + assert(find_first_of_literal == 1u); + + const auto find_first_of_literal_none = input.find_first_of(get_no_needle()); + assert(find_first_of_literal_none == str::npos); + + const auto find_first_of_literal_pos = input.find_first_of(get_cat(), 6); + assert(find_first_of_literal_pos == 13u); + + const auto find_first_of_literal_pos_none = input.find_first_of(get_no_needle(), 14); + assert(find_first_of_literal_pos_none == str::npos); + + const auto find_first_of_literal_overflow = input.find_first_of(get_cat(), 50); + assert(find_first_of_literal_overflow == str::npos); + + const auto find_first_of_literal_pos_count = input.find_first_of(get_cat(), 6, 4); + assert(find_first_of_literal_pos_count == 13u); + + const auto find_first_of_literal_pos_count_none = input.find_first_of(get_no_needle(), 14, 4); + assert(find_first_of_literal_pos_count_none == str::npos); + + const auto find_first_of_char = input.find_first_of(CharType{'e'}); + assert(find_first_of_char == 1u); + + const auto find_first_of_char_none = input.find_first_of(CharType{'x'}); + assert(find_first_of_char_none == str::npos); + + const auto find_first_of_char_pos = input.find_first_of(CharType{'e'}, 4); + assert(find_first_of_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_first_of_convertible = input.find_first_of(convertible); + assert(find_first_of_convertible == 0); + + const auto find_first_of_convertible_pos = input.find_first_of(convertible, 2); + assert(find_first_of_convertible_pos == 2u); + } + + { // find_first_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_not_of_str = input.find_first_not_of(needle); + assert(find_first_not_of_str == 0u); + + const auto find_first_not_of_str_none = input.find_first_not_of(input); + assert(find_first_not_of_str_none == str::npos); + + const auto find_first_not_of_str_pos = input.find_first_not_of(needle, 6); + assert(find_first_not_of_str_pos == 6u); + + const auto find_first_not_of_str_pos_none = input.find_first_not_of(input, 14); + assert(find_first_not_of_str_pos_none == str::npos); + + const auto find_first_not_of_str_overflow = input.find_first_not_of(needle, 50); + assert(find_first_not_of_str_overflow == str::npos); + + const auto find_first_not_of_literal = input.find_first_not_of(get_cat()); + assert(find_first_not_of_literal == 0u); + + const auto find_first_not_of_literal_none = input.find_first_not_of(get_literal_input()); + assert(find_first_not_of_literal_none == str::npos); + + const auto find_first_not_of_literal_pos = input.find_first_not_of(get_cat(), 6); + assert(find_first_not_of_literal_pos == 6u); + + const auto find_first_not_of_literal_pos_none = input.find_first_not_of(get_literal_input(), 2); + assert(find_first_not_of_literal_pos_none == str::npos); + + const auto find_first_not_of_literal_overflow = input.find_first_not_of(get_cat(), 50); + assert(find_first_not_of_literal_overflow == str::npos); + + const auto find_first_not_of_literal_pos_count = input.find_first_not_of(get_cat(), 6, 4); + assert(find_first_not_of_literal_pos_count == 6u); + + const auto find_first_not_of_literal_pos_count_none = + input.find_first_not_of(get_literal_input(), 14, 20); + assert(find_first_not_of_literal_pos_count_none == str::npos); + + const auto find_first_not_of_char = input.find_first_not_of(CharType{'H'}); + assert(find_first_not_of_char == 1u); + + const auto find_first_not_of_char_pos = input.find_first_not_of(CharType{'e'}, 1); + assert(find_first_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_first_not_of_convertible = input.find_first_not_of(convertible); + assert(find_first_not_of_convertible == str::npos); + + const auto find_first_not_of_convertible_pos = input.find_first_not_of(convertible, 2); + assert(find_first_not_of_convertible_pos == str::npos); + } + + { // find_last_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_of_str = input.find_last_of(needle); + assert(find_last_of_str == 18u); + + const auto find_last_of_str_none = input.find_last_of(no_needle); + assert(find_last_of_str_none == str::npos); + + const auto find_last_of_str_pos = input.find_last_of(needle, 6); + assert(find_last_of_str_pos == 1u); + + const auto find_last_of_str_pos_none = input.find_last_of(no_needle, 14); + assert(find_last_of_str_pos_none == str::npos); + + const auto find_last_of_str_overflow = input.find_last_of(needle, 50); + assert(find_last_of_str_overflow == 18u); + + const auto find_last_of_literal = input.find_last_of(get_cat()); + assert(find_last_of_literal == 18u); + + const auto find_last_of_literal_none = input.find_last_of(get_no_needle()); + assert(find_last_of_literal_none == str::npos); + + const auto find_last_of_literal_pos = input.find_last_of(get_cat(), 6); + assert(find_last_of_literal_pos == 1u); + + const auto find_last_of_literal_pos_none = input.find_last_of(get_no_needle(), 14); + assert(find_last_of_literal_pos_none == str::npos); + + const auto find_last_of_literal_overflow = input.find_last_of(get_cat(), 50); + assert(find_last_of_literal_overflow == 18u); + + const auto find_last_of_literal_pos_count = input.find_last_of(get_cat(), 6, 7); + assert(find_last_of_literal_pos_count == 1u); + + const auto find_last_of_literal_pos_count_none = input.find_last_of(get_no_needle(), 14, 4); + assert(find_last_of_literal_pos_count_none == str::npos); + + const auto find_last_of_char = input.find_last_of(CharType{'e'}); + assert(find_last_of_char == 17u); + + const auto find_last_of_char_none = input.find_last_of(CharType{'x'}); + assert(find_last_of_char_none == str::npos); + + const auto find_last_of_char_pos = input.find_last_of(CharType{'e'}, 4); + assert(find_last_of_char_pos == 1u); + + const string_view_convertible convertible; + const auto find_last_of_convertible = input.find_last_of(convertible); + assert(find_last_of_convertible == 19u); + + const auto find_last_of_convertible_pos = input.find_last_of(convertible, 4); + assert(find_last_of_convertible_pos == 4u); + } + + { // find_last_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_not_of_str = input.find_last_not_of(needle); + assert(find_last_not_of_str == 19u); + + const auto find_last_not_of_str_none = input.find_last_not_of(input); + assert(find_last_not_of_str_none == str::npos); + + const auto find_last_not_of_str_pos = input.find_last_not_of(needle, 6); + assert(find_last_not_of_str_pos == 6u); + + const auto find_last_not_of_str_pos_none = input.find_last_not_of(input, 14); + assert(find_last_not_of_str_pos_none == str::npos); + + const auto find_last_not_of_str_overflow = input.find_last_not_of(needle, 50); + assert(find_last_not_of_str_overflow == 19u); + + const auto find_last_not_of_literal = input.find_last_not_of(get_cat()); + assert(find_last_not_of_literal == 19u); + + const auto find_last_not_of_literal_none = input.find_last_not_of(get_literal_input()); + assert(find_last_not_of_literal_none == str::npos); + + const auto find_last_not_of_literal_pos = input.find_last_not_of(get_cat(), 6); + assert(find_last_not_of_literal_pos == 6u); + + const auto find_last_not_of_literal_pos_none = input.find_last_not_of(get_literal_input(), 2); + assert(find_last_not_of_literal_pos_none == str::npos); + + const auto find_last_not_of_literal_overflow = input.find_last_not_of(get_cat(), 50); + assert(find_last_not_of_literal_overflow == 19u); + + const auto find_last_not_of_literal_pos_count = input.find_last_not_of(get_cat(), 6, 4); + assert(find_last_not_of_literal_pos_count == 6u); + + const auto find_last_not_of_literal_pos_count_none = + input.find_last_not_of(get_literal_input(), 14, 20); + assert(find_last_not_of_literal_pos_count_none == str::npos); + + const auto find_last_not_of_char = input.find_last_not_of(CharType{'H'}); + assert(find_last_not_of_char == 19u); + + const auto find_last_not_of_char_pos = input.find_last_not_of(CharType{'e'}, 2); + assert(find_last_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_last_not_of_convertible = input.find_last_not_of(convertible); + assert(find_last_not_of_convertible == str::npos); + + const auto find_last_not_of_convertible_pos = input.find_last_not_of(convertible, 2); + assert(find_last_not_of_convertible_pos == str::npos); + } + + { // operator+ + const str first = get_cat(); + const str second = get_dog(); + + const str op_str_str = first + second; + assert(equalRanges(op_str_str, "kittendog"sv)); + + const str op_str_literal = first + get_dog(); + assert(equalRanges(op_str_literal, "kittendog"sv)); + + const str op_str_char = first + CharType{'!'}; + assert(equalRanges(op_str_char, "kitten!"sv)); + + const str op_literal_str = get_cat() + second; + assert(equalRanges(op_literal_str, "kittendog"sv)); + + const str op_char_str = CharType{'!'} + second; + assert(equalRanges(op_char_str, "!dog"sv)); + + const str op_rstr_rstr = str{get_cat()} + str{get_dog()}; + assert(equalRanges(op_rstr_rstr, "kittendog"sv)); + + const str op_rstr_str = str{get_cat()} + second; + assert(equalRanges(op_rstr_str, "kittendog"sv)); + + const str op_rstr_literal = str{get_cat()} + get_dog(); + assert(equalRanges(op_rstr_literal, "kittendog"sv)); + + const str op_rstr_char = str{get_cat()} + CharType{'!'}; + assert(equalRanges(op_rstr_char, "kitten!"sv)); + + const str op_str_rstr = first + str{get_dog()}; + assert(equalRanges(op_str_rstr, "kittendog"sv)); + + const str op_literal_rstr = get_cat() + str{get_dog()}; + assert(equalRanges(op_literal_rstr, "kittendog"sv)); + + const str op_char_rstr = CharType{'!'} + str{get_dog()}; + assert(equalRanges(op_char_rstr, "!dog"sv)); + } + + { // comparison + str first(get_view_input()); + str second(get_view_input()); + str third{get_cat()}; + + const bool eq_str_str = first == second; + assert(eq_str_str); + + const bool ne_str_str = first != third; + assert(ne_str_str); + + const bool less_str_str = first < third; + assert(less_str_str); + + const bool less_eq_str_str = first <= third; + assert(less_eq_str_str); + + const bool greater_str_str = first > third; + assert(!greater_str_str); + + const bool greater_eq_str_str = first >= third; + assert(!greater_eq_str_str); + + const bool eq_str_literal = first == get_view_input(); + assert(eq_str_literal); + + const bool ne_str_literal = first != get_cat(); + assert(ne_str_literal); + + const bool less_str_literal = first < get_cat(); + assert(less_str_literal); + + const bool less_eq_str_literal = first <= get_cat(); + assert(less_eq_str_literal); + + const bool greater_str_literal = first > get_cat(); + assert(!greater_str_literal); + + const bool greater_eq_str_literal = first >= get_cat(); + assert(!greater_eq_str_literal); + + const bool eq_literal_str = get_view_input() == second; + assert(eq_literal_str); + + const bool ne_literal_str = get_view_input() != third; + assert(ne_literal_str); + + const bool less_literal_str = get_view_input() < third; + assert(less_literal_str); + + const bool less_eq_literal_str = get_view_input() <= third; + assert(less_eq_literal_str); + + const bool greater_literal_str = get_view_input() > third; + assert(!greater_literal_str); + + const bool greater_eq_literal_str = get_view_input() >= third; + assert(!greater_eq_literal_str); + + const strong_ordering spaceship_str_str_eq = first <=> second; + assert(spaceship_str_str_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_str_less = first <=> third; + assert(spaceship_str_str_less == strong_ordering::less); + + const strong_ordering spaceship_str_str_greater = third <=> first; + assert(spaceship_str_str_greater == strong_ordering::greater); + + const strong_ordering spaceship_str_literal_eq = first <=> get_view_input(); + assert(spaceship_str_literal_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_literal_less = first <=> get_cat(); + assert(spaceship_str_literal_less == strong_ordering::less); + + const strong_ordering spaceship_str_literal_greater = third <=> get_dog(); + assert(spaceship_str_literal_greater == strong_ordering::greater); + } + + { // basic_string_view conversion + str s = get_literal_input(); + basic_string_view sv = s; + assert(equalRanges(sv, "Hello fluffy kittens"sv)); + } +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 +#endif // __EDG__ + return true; +} + +_CONSTEXPR20_CONTAINER bool test_udls() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 + + assert(equalRanges("purr purr"s, "purr purr"sv)); +#ifdef __cpp_char8_t + assert(equalRanges(u8"purr purr"s, "purr purr"sv)); +#endif // __cpp_char8_t + assert(equalRanges(u"purr purr"s, "purr purr"sv)); + assert(equalRanges(U"purr purr"s, "purr purr"sv)); + assert(equalRanges(L"purr purr"s, "purr purr"sv)); + +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 +#endif // __EDG__ + return true; +} + +template +struct CharLikeType { + constexpr CharLikeType() = default; + constexpr CharLikeType(CharType cc) : c(cc) {} + CharType c; +}; + +template +_CONSTEXPR20_CONTAINER bool test_iterators() { +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; + str literal_constructed = get_literal_input(); + + { // assignment + auto it = literal_constructed.begin(); + auto it2 = literal_constructed.end(); + auto cit = literal_constructed.cbegin(); + auto cit2 = literal_constructed.cend(); + + it = it2; + cit = cit2; + } + + { // op-> + basic_string> bs{CharType{'x'}}; + auto it = bs.begin(); + auto c = it->c; + assert(c == CharType{'x'}); + + auto cit = bs.cbegin(); + auto cc = cit->c; + assert(cc == CharType{'x'}); + } + + { // increment + auto it = literal_constructed.begin(); + assert(*++it == CharType{'e'}); + assert(*it++ == CharType{'e'}); + assert(*it == CharType{'l'}); + + auto cit = literal_constructed.cbegin(); + assert(*++cit == CharType{'e'}); + assert(*cit++ == CharType{'e'}); + assert(*cit == CharType{'l'}); + } + + { // advance + auto it = literal_constructed.begin() + 2; + assert(*it == CharType{'l'}); + it += 2; + assert(*it == CharType{'o'}); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, 16.10p1 + it = 2 + it; + assert(*it == CharType{'f'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + + auto cit = literal_constructed.cbegin() + 2; + assert(*cit == CharType{'l'}); + cit += 2; + assert(*cit == CharType{'o'}); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, 16.10p1 + cit = 2 + cit; + assert(*cit == CharType{'f'}); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + } + + { // decrement + auto it = literal_constructed.end(); + assert(*--it == CharType{'s'}); + assert(*it-- == CharType{'s'}); + assert(*it == CharType{'n'}); + + auto cit = literal_constructed.cend(); + assert(*--cit == CharType{'s'}); + assert(*cit-- == CharType{'s'}); + assert(*cit == CharType{'n'}); + } + + { // advance back + auto it = literal_constructed.end() - 2; + assert(*it == CharType{'n'}); + it -= 2; + assert(*it == CharType{'t'}); + + auto cit = literal_constructed.cend() - 2; + assert(*cit == CharType{'n'}); + cit -= 2; + assert(*cit == CharType{'t'}); + } + + { // difference + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.end(); + assert(it2 - it1 == ssize(get_view_input())); + + const auto cit1 = literal_constructed.cbegin(); + const auto cit2 = literal_constructed.cend(); + assert(cit2 - cit1 == ssize(get_view_input())); + + assert(it2 - cit1 == ssize(get_view_input())); + assert(cit2 - it1 == ssize(get_view_input())); + } + + { // comparison + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.begin(); + const auto it3 = literal_constructed.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + + assert((it1 <=> it2) == strong_ordering::equal); + assert((it1 <=> it3) == strong_ordering::less); + assert((it3 <=> it1) == strong_ordering::greater); + } + + { // access + const auto it = literal_constructed.begin() + 2; + it[2] = CharType{'l'}; + assert(literal_constructed[4] == CharType{'l'}); + + const auto cit = literal_constructed.cbegin() + 2; + assert(cit[2] == CharType{'l'}); + } +#endif // __EDG__ +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 + return true; +} + +template +_CONSTEXPR20_CONTAINER bool test_growth() { +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 // TRANSITION, VSO-1269894 + using str = basic_string; +#ifndef __EDG__ // TRANSITION, VSO-1273296 + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(1008); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(8007); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.push_back(CharType{'b'}); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1275530 + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(3, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(7000, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 3, CharType{'b'}); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 7000, CharType{'b'}); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) +#endif // __EDG__ +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 0 + return true; +} + +int main() { + test_interface(); +#ifdef __cpp_char8_t + test_interface(); +#endif // __cpp_char8_t + test_interface(); + test_interface(); + test_interface(); + + test_udls(); + + test_iterators(); +#ifdef __cpp_char8_t + test_iterators(); +#endif // __cpp_char8_t + test_iterators(); + test_iterators(); + test_iterators(); + + test_growth(); +#ifdef __cpp_char8_t + test_growth(); +#endif // __cpp_char8_t + test_growth(); + test_growth(); + test_growth(); + +#ifdef __cpp_lib_constexpr_string + static_assert(test_interface()); +#ifdef __cpp_char8_t + static_assert(test_interface()); +#endif // __cpp_char8_t + static_assert(test_interface()); + static_assert(test_interface()); + static_assert(test_interface()); + + static_assert(test_udls()); + + static_assert(test_iterators()); +#ifdef __cpp_char8_t + static_assert(test_iterators()); +#endif // __cpp_char8_t + static_assert(test_iterators()); + static_assert(test_iterators()); + static_assert(test_iterators()); + + static_assert(test_growth()); +#ifdef __cpp_char8_t + static_assert(test_growth()); +#endif // __cpp_char8_t + static_assert(test_growth()); + static_assert(test_growth()); + static_assert(test_growth()); +#endif // __cpp_lib_constexpr_string +} diff --git a/tests/std/tests/VSO_0102478_moving_allocators/test.cpp b/tests/std/tests/VSO_0102478_moving_allocators/test.cpp index e0619b09e0b..41af2700a9e 100644 --- a/tests/std/tests/VSO_0102478_moving_allocators/test.cpp +++ b/tests/std/tests/VSO_0102478_moving_allocators/test.cpp @@ -65,72 +65,50 @@ void trigger_alloc_copied() { } struct test_info { - string type_name; - string test_name; + const char* type_name; + const char* test_name; int count; }; -using expected_table_t = vector; - -#pragma warning(push) -#pragma warning(disable : 4640) // 'variable': construction of local static object is not thread-safe #if _ITERATOR_DEBUG_LEVEL == 0 - -const expected_table_t& get_expected_moves_table() { - static const expected_table_t s_expected_moves{ - {"*", "*", 1}, - {"unordered_set", "*", - 2}, // unordered_* moves two allocators, one for list, and one for vector of list iterators. - {"unordered_multiset", "*", 2}, - {"unordered_map", "*", 2}, - {"unordered_multimap", "*", 2}, - }; - - return s_expected_moves; -} - -const expected_table_t& get_expected_copies_table() { - static const expected_table_t s_expected_copies{ - {"*", "*", 0}, {"deque", "*", 1}, // deque always causes an additional copy due to creating the proxy allocator - }; - - return s_expected_copies; -} - +constexpr array expected_moves_table{{ + {"*", "*", 1}, + {"unordered_set", "*", 2}, // unordered_* moves two allocators, one for list, and one for vector of list iterators. + {"unordered_multiset", "*", 2}, + {"unordered_map", "*", 2}, + {"unordered_multimap", "*", 2}, +}}; + +constexpr array expected_copies_table{{ + {"*", "*", 0}, // + {"deque", "*", 1}, // deque always causes an additional copy due to creating the proxy allocator +}}; #else // _ITERATOR_DEBUG_LEVEL == 0 - -const expected_table_t& get_expected_moves_table() { - static const expected_table_t s_expected_moves{ - {"*", "*", 1}, - {"unordered_set", "*", - 2}, // unordered_* have two allocators, so copy two proxy allocators and have two move operations - {"unordered_multiset", "*", 2}, - {"unordered_map", "*", 2}, - {"unordered_multimap", "*", 2}, - }; - - return s_expected_moves; -} - -const expected_table_t& get_expected_copies_table() { - static const expected_table_t s_expected_copies{ - {"*", "*", 1}, // IDL causes an additional copy due to a proxy allocator being created. - {"vector", "bool", 2}, // with IDL>0, _Vb_val also allocates a proxy, so the extra copy is copying the allocator - // for that purpose - {"unordered_set", "*", - 2}, // unordered_* have two allocators, so copy two proxy allocators and have two move operations - {"unordered_multiset", "*", 2}, - {"unordered_map", "*", 2}, - {"unordered_multimap", "*", 2}, - }; - - return s_expected_copies; -} - +constexpr array expected_moves_table{{ + {"*", "*", 1}, + {"unordered_set", "*", 2}, // unordered_* have two allocators, + // so copy two proxy allocators and have two move operations + {"unordered_multiset", "*", 2}, + {"unordered_map", "*", 2}, + {"unordered_multimap", "*", 2}, +}}; + +constexpr array expected_copies_table{{ + {"*", "*", 1}, // IDL causes an additional copy due to a proxy allocator being created. + {"vector", "bool", 2}, // with IDL>0, _Vb_val also allocates a proxy, + // so the extra copy is copying the allocator for that purpose + {"unordered_set", "*", 2}, // unordered_* have two allocators, + // so copy two proxy allocators and have two move operations + {"unordered_multiset", "*", 2}, + {"unordered_map", "*", 2}, + {"unordered_multimap", "*", 2}, +}}; #endif // _ITERATOR_DEBUG_LEVEL == 0 -#pragma warning(pop) -int query_expected_table(const expected_table_t& table, const string& type_name, const string& test_name) { +template +int query_expected_table(const ExpectedTable& table, const string& type_name, const string& test_name) { + const string star{"*"}; + auto exact_match = find_if(begin(table), end(table), [&](const test_info& other) { return type_name == other.type_name && test_name == other.test_name; }); @@ -139,14 +117,14 @@ int query_expected_table(const expected_table_t& table, const string& type_name, } auto fuzzy_match = find_if(begin(table), end(table), - [&](const test_info& other) { return type_name == other.type_name && "*" == other.test_name; }); + [&](const test_info& other) { return type_name == other.type_name && star == other.test_name; }); if (fuzzy_match != end(table)) { return fuzzy_match->count; } auto fuzziest_match = find_if(begin(table), end(table), - [](const test_info& other) { return "*" == other.type_name && "*" == other.test_name; }); + [&](const test_info& other) { return star == other.type_name && star == other.test_name; }); if (fuzziest_match != end(table)) { return fuzziest_match->count; @@ -156,11 +134,11 @@ int query_expected_table(const expected_table_t& table, const string& type_name, } int get_expected_copies(const string& type_name, const string& test_name) { - return query_expected_table(get_expected_copies_table(), type_name, test_name); + return query_expected_table(expected_copies_table, type_name, test_name); } int get_expected_moves(const string& type_name, const string& test_name) { - return query_expected_table(get_expected_moves_table(), type_name, test_name); + return query_expected_table(expected_moves_table, type_name, test_name); } template diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index d9b94d93205..fae3753143c 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -481,6 +481,20 @@ STATIC_ASSERT(__cpp_lib_constexpr_numeric == 201911L); #endif #endif +#if _HAS_CXX20 && !defined(__clang__) // TRANSITION, LLVM-48606 +#ifndef __cpp_lib_constexpr_string +#error __cpp_lib_constexpr_string is not defined +#elif __cpp_lib_constexpr_string != 201907L +#error __cpp_lib_constexpr_string is not 201907L +#else +STATIC_ASSERT(__cpp_lib_constexpr_string == 201907L); +#endif +#else +#ifdef __cpp_lib_constexpr_string +#error __cpp_lib_constexpr_string is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_constexpr_string_view #error __cpp_lib_constexpr_string_view is not defined