Instead of cloning the string and then setting its length to zero, just point to the empty rep instead.
I'm not sure if this is conforming in C++03, because it modifies the capacity(), whereas clear() is specified as if it called erase(begin(), end()), which typically wouldn't alter the capacity (but I'm not sure whether it's allowed to alter it). It passes all tests, but I'm not planning to commit it.
commit 83de17363e8978c4ee6af499aaab9e3734863449 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Jun 3 20:23:10 2014 +0100 PR libstdc++/56166 * include/bits/basic_string.h (basic_string::clear()): Drop reference and use empty rep. * include/ext/rc_string_base.h (__rc_string_base::_M_clear()): Likewise. * testsuite/21_strings/basic_string/56166.cc: New. * testsuite/ext/vstring/modifiers/clear/56166.cc: New. diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index cd60376..420ead5 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -808,10 +808,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /** * Erases the string, making it empty. */ +#if _GLIBCXX_FULLY_DYNAMIC_STRING == 0 + void + clear() _GLIBCXX_NOEXCEPT + { + _M_rep()->_M_dispose(this->get_allocator()); + _M_data(_S_empty_rep()._M_refdata()); + } +#else // PR 56166: this should not throw. void clear() { _M_mutate(0, this->size(), 0); } +#endif /** * Returns true if the %string is empty. Equivalent to diff --git a/libstdc++-v3/include/ext/rc_string_base.h b/libstdc++-v3/include/ext/rc_string_base.h index 4c72407..e9df075 100644 --- a/libstdc++-v3/include/ext/rc_string_base.h +++ b/libstdc++-v3/include/ext/rc_string_base.h @@ -354,7 +354,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void _M_clear() - { _M_erase(size_type(0), _M_length()); } + { + _M_dispose(); + _M_data(_S_empty_rep._M_refcopy()); + } bool _M_compare(const __rc_string_base&) const diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc b/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc new file mode 100644 index 0000000..b680afa --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/56166.cc @@ -0,0 +1,90 @@ +// Copyright (C) 2014 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options " -std=gnu++11 " } + +// libstdc++/56166 + +#include <string> +#include <new> + +static int fail_after = -1; + +template<typename T> + struct Allocator + { + using value_type = T; + + // shouldn't need these typedefs, but <string> doesn't use allocator_traits + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using difference_type = long; + using size_type = unsigned long; + template<typename U> + struct rebind { + using other = Allocator<U>; + }; + + Allocator() { } + + template<typename U> + Allocator(const Allocator<U>&) { } + + T* allocate(size_type n) + { + if (fail_after >= 0) { + if (fail_after-- == 0) { + throw std::bad_alloc(); + } + } + return (T*)new char[n * sizeof(T)]; + } + + void deallocate(T* p, size_type) + { + delete[] (char*)p; + } + }; + +template<typename T> + bool operator==(const Allocator<T>&, const Allocator<T>&) { return true; } +template<typename T> + bool operator!=(const Allocator<T>&, const Allocator<T>&) { return false; } + +using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>; + +string f() +{ + string s1("xxxxxx"); + string s2 = s1; + s1.clear(); + return s2; +} + +int main() +{ + for (int i = 0; i < 10; i++) { + try { + fail_after = i; + f(); + break; + } catch (std::bad_alloc) { + } + } +} diff --git a/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc new file mode 100644 index 0000000..0217023 --- /dev/null +++ b/libstdc++-v3/testsuite/ext/vstring/modifiers/clear/56166.cc @@ -0,0 +1,93 @@ +// Copyright (C) 2014 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options " -std=gnu++11 " } + +// libstdc++/56166 + +#include <ext/vstring.h> +#include <new> + +static int fail_after = -1; + +template<typename T> + struct Allocator + { + using value_type = T; + + // shouldn't need these typedefs, but <string> doesn't use allocator_traits + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using difference_type = long; + using size_type = unsigned long; + template<typename U> + struct rebind { + using other = Allocator<U>; + }; + + Allocator() { } + + template<typename U> + Allocator(const Allocator<U>&) { } + + T* allocate(size_type n) + { + if (fail_after >= 0) { + if (fail_after-- == 0) { + throw std::bad_alloc(); + } + } + return (T*)new char[n * sizeof(T)]; + } + + void deallocate(T* p, size_type) + { + delete[] (char*)p; + } + }; + +template<typename T> + bool operator==(const Allocator<T>&, const Allocator<T>&) { return true; } +template<typename T> + bool operator!=(const Allocator<T>&, const Allocator<T>&) { return false; } + + +using string = __gnu_cxx::__versa_string<char, std::char_traits<char>, + Allocator<char>, + __gnu_cxx::__rc_string_base>; + +string f() +{ + string s1("xxxxxx"); + string s2 = s1; + s1.clear(); + return s2; +} + +int main() +{ + for (int i = 0; i < 10; i++) { + try { + fail_after = i; + f(); + break; + } catch (std::bad_alloc) { + } + } +}