diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -38,6 +38,7 @@ Implemented Papers ------------------ - P2499R0 - ``string_view`` range constructor should be ``explicit`` +- P2438R2 - ``std::string::substr() &&`` Improvements and New Features ----------------------------- diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -104,6 +104,8 @@ const allocator_type& a = allocator_type()); // constexpr since C++20 basic_string(const basic_string& str, size_type pos, size_type n, const Allocator& a = Allocator()); // constexpr since C++20 + constexpr basic_string(basic_string&& str, size_type pos, const Allocator& a = Allocator()); // since C++23 + constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); // since C++23 template basic_string(const T& t, size_type pos, size_type n, const Allocator& a = Allocator()); // C++17, constexpr since C++20 template @@ -256,8 +258,8 @@ basic_string& replace(const_iterator i1, const_iterator i2, initializer_list); // constexpr since C++20 size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20 - basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20 - + basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20, const & since C++23 + constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&; // since C++23 void swap(basic_string& str) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value); // C++17, constexpr since C++20 @@ -864,11 +866,32 @@ basic_string(size_type __n, _CharT __c, const _Allocator& __a); _LIBCPP_CONSTEXPR_AFTER_CXX17 - basic_string(const basic_string& __str, size_type __pos, size_type __n, - const _Allocator& __a = _Allocator()); + basic_string(const basic_string& __str, size_type __pos, size_type __n, const _Allocator& __a = _Allocator()); _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 - basic_string(const basic_string& __str, size_type __pos, - const _Allocator& __a = _Allocator()); + basic_string(const basic_string& __str, size_type __pos, const _Allocator& __a = _Allocator()); + +#if _LIBCPP_STD_VER > 20 + _LIBCPP_HIDE_FROM_ABI constexpr + basic_string(basic_string&& __str, size_type __pos, const _Allocator& __alloc = _Allocator()) + : basic_string(std::move(__str), __pos, npos, __alloc) {} + + _LIBCPP_HIDE_FROM_ABI constexpr + basic_string(basic_string&& __str, size_type __pos, size_type __n, const _Allocator& __alloc = _Allocator()) + : __r_(__default_init_tag(), __alloc) { + if (__pos > __str.size()) + __throw_out_of_range(); + auto __len = std::min(__n, __str.size() - __pos); + if (__alloc_traits::is_always_equal::value || __alloc == __str.__alloc()) { + __r_.first() = __str.__r_.first(); + __str.__default_init(); + _Traits::move(data(), data() + __pos, __len); + __set_size(__len); + _Traits::assign(data()[__len], value_type()); + } else { + __init(__str.data() + __pos, __len); + } + } +#endif template::value && !__is_same_uncvref<_Tp, basic_string>::value> > _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_AFTER_CXX17 @@ -1261,8 +1284,23 @@ #endif // _LIBCPP_CXX03_LANG _LIBCPP_CONSTEXPR_AFTER_CXX17 size_type copy(value_type* __s, size_type __n, size_type __pos = 0) const; + +#if _LIBCPP_STD_VER <= 20 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 - basic_string substr(size_type __pos = 0, size_type __n = npos) const; + basic_string substr(size_type __pos = 0, size_type __n = npos) const { + return basic_string(*this, __pos, __n, __alloc()); + } +#else + _LIBCPP_HIDE_FROM_ABI constexpr + basic_string substr(size_type __pos = 0, size_type __n = npos) const& { + return basic_string(*this, __pos, __n, __alloc()); + } + + _LIBCPP_HIDE_FROM_ABI constexpr + basic_string substr(size_type __pos = 0, size_type __n = npos) && { + return basic_string(std::move(*this), __pos, __n, __alloc()); + } +#endif _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 void swap(basic_string& __str) @@ -3565,14 +3603,6 @@ return __rlen; } -template -inline _LIBCPP_CONSTEXPR_AFTER_CXX17 -basic_string<_CharT, _Traits, _Allocator> -basic_string<_CharT, _Traits, _Allocator>::substr(size_type __pos, size_type __n) const -{ - return basic_string(*this, __pos, __n, __alloc()); -} - template inline _LIBCPP_CONSTEXPR_AFTER_CXX17 void diff --git a/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp @@ -0,0 +1,166 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr basic_string(basic_string&& str, size_type pos, const Allocator& a = Allocator()); +// constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); + +#include +#include + +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_macros.h" + +template +constexpr void test_string_pos(S orig, size_t pos, S expected) { + if (pos <= orig.size()) { + S substr(std::move(orig), pos); + LIBCPP_ASSERT(orig.__invariants()); + assert(substr == expected); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + else if (!std::is_constant_evaluated()) { + try { + [[maybe_unused]] S substr = S(std::move(orig), pos); + assert(false); + } catch (const std::out_of_range&) { + assert(expected == "exception"); + } + } +#endif +} + +template +constexpr void test_string_pos_alloc(S orig, size_t pos, const typename S::allocator_type& alloc, S expected) { + if (pos <= orig.size()) { + S substr(std::move(orig), pos, alloc); + LIBCPP_ASSERT(orig.__invariants()); + assert(substr == expected); + assert(substr.get_allocator() == alloc); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + else if (!std::is_constant_evaluated()) { + try { + [[maybe_unused]] S substr = S(std::move(orig), pos); + assert(false); + } catch (const std::out_of_range&) { + assert(expected == "exception"); + } + } +#endif +} + +template +constexpr void test_string_pos_n(S orig, size_t pos, size_t n, S expected) { + if (pos <= orig.size()) { + S substr(std::move(orig), pos, n); + LIBCPP_ASSERT(orig.__invariants()); + assert(substr == expected); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + else if (!std::is_constant_evaluated()) { + try { + [[maybe_unused]] S substr = S(std::move(orig), pos, n); + assert(false); + } catch (const std::out_of_range&) { + assert(expected == "exception"); + } + } +#endif +} + +template +constexpr void +test_string_pos_n_alloc(S orig, size_t pos, size_t n, const typename S::allocator_type& alloc, S expected) { + if (pos <= orig.size()) { + S substr(std::move(orig), pos, n, alloc); + LIBCPP_ASSERT(orig.__invariants()); + assert(substr == expected); + assert(substr.get_allocator() == alloc); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + else if (!std::is_constant_evaluated()) { + try { + [[maybe_unused]] S substr = S(std::move(orig), pos, n, alloc); + assert(false); + } catch (const std::out_of_range&) { + assert(expected == "exception"); + } + } +#endif +} + +template +constexpr void test_string(const typename S::allocator_type& alloc) { + test_string_pos("", 0, ""); + test_string_pos("", 1, "exception"); + test_string_pos("Banane", 1, "anane"); + test_string_pos("Banane", 6, ""); + test_string_pos("Banane", 7, "exception"); + test_string_pos("long long string so no SSO", 0, "long long string so no SSO"); + test_string_pos("long long string so no SSO", 10, "string so no SSO"); + test_string_pos("long long string so no SSO", 26, ""); + test_string_pos("long long string so no SSO", 27, "exception"); + + test_string_pos_alloc("", 0, alloc, ""); + test_string_pos_alloc("", 1, alloc, "exception"); + test_string_pos_alloc("Banane", 1, alloc, "anane"); + test_string_pos_alloc("Banane", 6, alloc, ""); + test_string_pos_alloc("Banane", 7, alloc, "exception"); + test_string_pos_alloc("long long string so no SSO", 0, alloc, "long long string so no SSO"); + test_string_pos_alloc("long long string so no SSO", 10, alloc, "string so no SSO"); + test_string_pos_alloc("long long string so no SSO", 26, alloc, ""); + test_string_pos_alloc("long long string so no SSO", 27, alloc, "exception"); + + test_string_pos_n("", 0, 0, ""); + test_string_pos_n("", 0, 1, ""); + test_string_pos_n("", 1, 0, "exception"); + test_string_pos_n("", 1, 1, "exception"); + test_string_pos_n("Banane", 1, 10, "anane"); + test_string_pos_n("Banane", 6, 0, ""); + test_string_pos_n("Banane", 6, 5, ""); + test_string_pos_n("Banane", 7, 10, "exception"); + test_string_pos_n("long long string so no SSO", 0, 10, "long long "); + test_string_pos_n("long long string so no SSO", 10, 8, "string s"); + test_string_pos_n("long long string so no SSO", 20, 10, "no SSO"); + test_string_pos_n("long long string so no SSO", 26, 10, ""); + test_string_pos_n("long long string so no SSO", 27, 10, "exception"); + + test_string_pos_n_alloc("", 0, 0, alloc, ""); + test_string_pos_n_alloc("", 0, 1, alloc, ""); + test_string_pos_n_alloc("", 1, 0, alloc, "exception"); + test_string_pos_n_alloc("", 1, 1, alloc, "exception"); + test_string_pos_n_alloc("Banane", 1, 10, alloc, "anane"); + test_string_pos_n_alloc("Banane", 6, 0, alloc, ""); + test_string_pos_n_alloc("Banane", 6, 5, alloc, ""); + test_string_pos_n_alloc("Banane", 7, 10, alloc, "exception"); + test_string_pos_n_alloc("long long string so no SSO", 0, 10, alloc, "long long "); + test_string_pos_n_alloc("long long string so no SSO", 10, 8, alloc, "string s"); + test_string_pos_n_alloc("long long string so no SSO", 20, 10, alloc, "no SSO"); + test_string_pos_n_alloc("long long string so no SSO", 26, 10, alloc, ""); + test_string_pos_n_alloc("long long string so no SSO", 27, 10, alloc, "exception"); +} + +constexpr bool test() { + test_string(std::allocator{}); + test_string, min_allocator>>(min_allocator{}); + test_string, test_allocator>>(test_allocator{42}); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp --- a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp @@ -8,7 +8,7 @@ // -// basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20 +// basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20, const & since C++23 #include #include @@ -47,130 +47,72 @@ #endif } +template +TEST_CONSTEXPR_CXX20 void test_string() { + test(S(""), 0, 0); + test(S(""), 1, 0); + test(S("pniot"), 0, 0); + test(S("htaob"), 0, 1); + test(S("fodgq"), 0, 2); + test(S("hpqia"), 0, 4); + test(S("qanej"), 0, 5); + test(S("dfkap"), 1, 0); + test(S("clbao"), 1, 1); + test(S("ihqrf"), 1, 2); + test(S("mekdn"), 1, 3); + test(S("ngtjf"), 1, 4); + test(S("srdfq"), 2, 0); + test(S("qkdrs"), 2, 1); + test(S("ikcrq"), 2, 2); + test(S("cdaih"), 2, 3); + test(S("dmajb"), 4, 0); + test(S("karth"), 4, 1); + test(S("lhcdo"), 5, 0); + test(S("acbsj"), 6, 0); + test(S("pbsjikaole"), 0, 0); + test(S("pcbahntsje"), 0, 1); + test(S("mprdjbeiak"), 0, 5); + test(S("fhepcrntko"), 0, 9); + test(S("eqmpaidtls"), 0, 10); + test(S("joidhalcmq"), 1, 0); + test(S("omigsphflj"), 1, 1); + test(S("kocgbphfji"), 1, 4); + test(S("onmjekafbi"), 1, 8); + test(S("fbslrjiqkm"), 1, 9); + test(S("oqmrjahnkg"), 5, 0); + test(S("jeidpcmalh"), 5, 1); + test(S("schfalibje"), 5, 2); + test(S("crliponbqe"), 5, 4); + test(S("igdscopqtm"), 5, 5); + test(S("qngpdkimlc"), 9, 0); + test(S("thdjgafrlb"), 9, 1); + test(S("hcjitbfapl"), 10, 0); + test(S("mgojkldsqh"), 11, 0); + test(S("gfshlcmdjreqipbontak"), 0, 0); + test(S("nadkhpfemgclosibtjrq"), 0, 1); + test(S("nkodajteqplrbifhmcgs"), 0, 10); + test(S("ofdrqmkeblthacpgijsn"), 0, 19); + test(S("gbmetiprqdoasckjfhln"), 0, 20); + test(S("bdfjqgatlksriohemnpc"), 1, 0); + test(S("crnklpmegdqfiashtojb"), 1, 1); + test(S("ejqcnahdrkfsmptilgbo"), 1, 9); + test(S("jsbtafedocnirgpmkhql"), 1, 18); + test(S("prqgnlbaejsmkhdctoif"), 1, 19); + test(S("qnmodrtkebhpasifgcjl"), 10, 0); + test(S("pejafmnokrqhtisbcdgl"), 10, 1); + test(S("cpebqsfmnjdolhkratgi"), 10, 5); + test(S("odnqkgijrhabfmcestlp"), 10, 9); + test(S("lmofqdhpkibagnrcjste"), 10, 10); + test(S("lgjqketopbfahrmnsicd"), 19, 0); + test(S("ktsrmnqagdecfhijpobl"), 19, 1); + test(S("lsaijeqhtrbgcdmpfkno"), 20, 0); + test(S("dplqartnfgejichmoskb"), 21, 0); +} + TEST_CONSTEXPR_CXX20 bool test() { - { - typedef std::string S; - test(S(""), 0, 0); - test(S(""), 1, 0); - test(S("pniot"), 0, 0); - test(S("htaob"), 0, 1); - test(S("fodgq"), 0, 2); - test(S("hpqia"), 0, 4); - test(S("qanej"), 0, 5); - test(S("dfkap"), 1, 0); - test(S("clbao"), 1, 1); - test(S("ihqrf"), 1, 2); - test(S("mekdn"), 1, 3); - test(S("ngtjf"), 1, 4); - test(S("srdfq"), 2, 0); - test(S("qkdrs"), 2, 1); - test(S("ikcrq"), 2, 2); - test(S("cdaih"), 2, 3); - test(S("dmajb"), 4, 0); - test(S("karth"), 4, 1); - test(S("lhcdo"), 5, 0); - test(S("acbsj"), 6, 0); - test(S("pbsjikaole"), 0, 0); - test(S("pcbahntsje"), 0, 1); - test(S("mprdjbeiak"), 0, 5); - test(S("fhepcrntko"), 0, 9); - test(S("eqmpaidtls"), 0, 10); - test(S("joidhalcmq"), 1, 0); - test(S("omigsphflj"), 1, 1); - test(S("kocgbphfji"), 1, 4); - test(S("onmjekafbi"), 1, 8); - test(S("fbslrjiqkm"), 1, 9); - test(S("oqmrjahnkg"), 5, 0); - test(S("jeidpcmalh"), 5, 1); - test(S("schfalibje"), 5, 2); - test(S("crliponbqe"), 5, 4); - test(S("igdscopqtm"), 5, 5); - test(S("qngpdkimlc"), 9, 0); - test(S("thdjgafrlb"), 9, 1); - test(S("hcjitbfapl"), 10, 0); - test(S("mgojkldsqh"), 11, 0); - test(S("gfshlcmdjreqipbontak"), 0, 0); - test(S("nadkhpfemgclosibtjrq"), 0, 1); - test(S("nkodajteqplrbifhmcgs"), 0, 10); - test(S("ofdrqmkeblthacpgijsn"), 0, 19); - test(S("gbmetiprqdoasckjfhln"), 0, 20); - test(S("bdfjqgatlksriohemnpc"), 1, 0); - test(S("crnklpmegdqfiashtojb"), 1, 1); - test(S("ejqcnahdrkfsmptilgbo"), 1, 9); - test(S("jsbtafedocnirgpmkhql"), 1, 18); - test(S("prqgnlbaejsmkhdctoif"), 1, 19); - test(S("qnmodrtkebhpasifgcjl"), 10, 0); - test(S("pejafmnokrqhtisbcdgl"), 10, 1); - test(S("cpebqsfmnjdolhkratgi"), 10, 5); - test(S("odnqkgijrhabfmcestlp"), 10, 9); - test(S("lmofqdhpkibagnrcjste"), 10, 10); - test(S("lgjqketopbfahrmnsicd"), 19, 0); - test(S("ktsrmnqagdecfhijpobl"), 19, 1); - test(S("lsaijeqhtrbgcdmpfkno"), 20, 0); - test(S("dplqartnfgejichmoskb"), 21, 0); - } + test_string(); #if TEST_STD_VER >= 11 - { - typedef std::basic_string, min_allocator> S; - test(S(""), 0, 0); - test(S(""), 1, 0); - test(S("pniot"), 0, 0); - test(S("htaob"), 0, 1); - test(S("fodgq"), 0, 2); - test(S("hpqia"), 0, 4); - test(S("qanej"), 0, 5); - test(S("dfkap"), 1, 0); - test(S("clbao"), 1, 1); - test(S("ihqrf"), 1, 2); - test(S("mekdn"), 1, 3); - test(S("ngtjf"), 1, 4); - test(S("srdfq"), 2, 0); - test(S("qkdrs"), 2, 1); - test(S("ikcrq"), 2, 2); - test(S("cdaih"), 2, 3); - test(S("dmajb"), 4, 0); - test(S("karth"), 4, 1); - test(S("lhcdo"), 5, 0); - test(S("acbsj"), 6, 0); - test(S("pbsjikaole"), 0, 0); - test(S("pcbahntsje"), 0, 1); - test(S("mprdjbeiak"), 0, 5); - test(S("fhepcrntko"), 0, 9); - test(S("eqmpaidtls"), 0, 10); - test(S("joidhalcmq"), 1, 0); - test(S("omigsphflj"), 1, 1); - test(S("kocgbphfji"), 1, 4); - test(S("onmjekafbi"), 1, 8); - test(S("fbslrjiqkm"), 1, 9); - test(S("oqmrjahnkg"), 5, 0); - test(S("jeidpcmalh"), 5, 1); - test(S("schfalibje"), 5, 2); - test(S("crliponbqe"), 5, 4); - test(S("igdscopqtm"), 5, 5); - test(S("qngpdkimlc"), 9, 0); - test(S("thdjgafrlb"), 9, 1); - test(S("hcjitbfapl"), 10, 0); - test(S("mgojkldsqh"), 11, 0); - test(S("gfshlcmdjreqipbontak"), 0, 0); - test(S("nadkhpfemgclosibtjrq"), 0, 1); - test(S("nkodajteqplrbifhmcgs"), 0, 10); - test(S("ofdrqmkeblthacpgijsn"), 0, 19); - test(S("gbmetiprqdoasckjfhln"), 0, 20); - test(S("bdfjqgatlksriohemnpc"), 1, 0); - test(S("crnklpmegdqfiashtojb"), 1, 1); - test(S("ejqcnahdrkfsmptilgbo"), 1, 9); - test(S("jsbtafedocnirgpmkhql"), 1, 18); - test(S("prqgnlbaejsmkhdctoif"), 1, 19); - test(S("qnmodrtkebhpasifgcjl"), 10, 0); - test(S("pejafmnokrqhtisbcdgl"), 10, 1); - test(S("cpebqsfmnjdolhkratgi"), 10, 5); - test(S("odnqkgijrhabfmcestlp"), 10, 9); - test(S("lmofqdhpkibagnrcjste"), 10, 10); - test(S("lgjqketopbfahrmnsicd"), 19, 0); - test(S("ktsrmnqagdecfhijpobl"), 19, 1); - test(S("lsaijeqhtrbgcdmpfkno"), 20, 0); - test(S("dplqartnfgejichmoskb"), 21, 0); - } + test_string, min_allocator>>(); #endif return true; diff --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&; + +#include +#include + +#include "min_allocator.h" +#include "test_allocator.h" + +template +constexpr void test(S orig, size_t pos, ptrdiff_t n, S expected) { + if (pos <= orig.size()) { + auto orig_string_copy = orig; + auto rlen = std::min(n, orig.size() - pos); + S str = std::move(orig).substr(pos, n); + LIBCPP_ASSERT(orig.__invariants()); + LIBCPP_ASSERT(str.__invariants()); + assert(str.size() == rlen); + assert(str == expected); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + else if (!std::is_constant_evaluated()) { + try { + S str = std::move(orig).substr(pos, n); + assert(false); + } catch (const std::out_of_range&) { + assert(expected == "exception"); + } + } +#endif +} + +template +constexpr void test_string() { + test("", 0, 0, ""); + test("", 0, 1, ""); + test("", 1, 0, "exception"); + test("", 1, 1, "exception"); + test("short string", 0, 1, "s"); + test("short string", 5, 5, " stri"); + test("short string", 12, 5, ""); + test("short string", 13, 5, "exception"); + test("long long string so no SSO", 0, 0, ""); + test("long long string so no SSO", 0, 10, "long long "); + test("long long string so no SSO", 10, 10, "string so "); + test("long long string so no SSO", 20, 10, "no SSO"); + test("long long string so no SSO", 26, 10, ""); + test("long long string so no SSO", 27, 0, "exception"); +} + +constexpr bool test() { + test_string(); + test_string, min_allocator>>(); + test_string, test_allocator>>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}