diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2017-01-27 16:17:04 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2017-01-27 16:17:04 +0000 |
commit | a7d47f352663d16c12cb3a40459b546d166023d7 (patch) | |
tree | cf112fca8fe937bfad8a4ba585c171cccc678a55 /libstdc++-v3/include/bits/basic_string.tcc | |
parent | f75bbf3fc68e1e03b55ae1f60c861faf213968ab (diff) | |
download | gcc-a7d47f352663d16c12cb3a40459b546d166023d7.tar.gz |
PR libstdc++/79254 fix exception-safety in std::string::operator=
PR libstdc++/79254
* config/abi/pre/gnu.ver: Add new symbols.
* include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI]
(basic_string::_M_copy_assign): New overloaded functions to perform
copy assignment.
(basic_string::operator=(const basic_string&)): Dispatch to
_M_copy_assign.
* include/bits/basic_string.tcc [_GLIBCXX_USE_CXX11_ABI]
(basic_string::_M_copy_assign(const basic_string&, true_type)):
Define, performing rollback on exception.
* testsuite/21_strings/basic_string/allocator/char/copy_assign.cc:
Test exception-safety guarantee.
* testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc:
Likewise.
* testsuite/util/testsuite_allocator.h (uneq_allocator::swap): Make
std::swap visible.
From-SVN: r244986
Diffstat (limited to 'libstdc++-v3/include/bits/basic_string.tcc')
-rw-r--r-- | libstdc++-v3/include/bits/basic_string.tcc | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc index 41b7fa196b0..adc8b853137 100644 --- a/libstdc++-v3/include/bits/basic_string.tcc +++ b/libstdc++-v3/include/bits/basic_string.tcc @@ -275,6 +275,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } +#if __cplusplus >= 201103L + template<typename _CharT, typename _Traits, typename _Alloc> + void + basic_string<_CharT, _Traits, _Alloc>:: + _M_copy_assign(const basic_string& __str, true_type) + { + struct _Guard // RAII type for strong exception-safety guarantee. + { + // Takes ownership of string's original state. + _Guard(basic_string* __self) + : _M_self(__self), _M_alloc(std::move(__self->_M_get_allocator())), + _M_ptr(__self->_M_data()), + _M_capacity(__self->_M_allocated_capacity), _M_len(__self->length()) + { + __self->_M_data(__self->_M_local_data()); + __self->_M_length(0); + } + + // Restores string's original state if _M_release() was not called. + ~_Guard() + { + if (_M_ptr) + { + _M_self->_M_get_allocator() = std::move(_M_alloc); + _M_self->_M_data(_M_ptr); + _M_self->_M_capacity(_M_capacity); + _M_self->_M_length(_M_len); + } + } + + _Guard(const _Guard&) = delete; + _Guard& operator=(const _Guard&) = delete; + + void _M_release() + { + // Original state can be freed now. + _Alloc_traits::deallocate(_M_alloc, _M_ptr, _M_capacity + 1); + _M_ptr = nullptr; + } + + basic_string* _M_self; + allocator_type _M_alloc; + pointer _M_ptr; + size_type _M_capacity; + size_type _M_len; + }; + + if (!_Alloc_traits::_S_always_equal() && !_M_is_local() + && _M_get_allocator() != __str._M_get_allocator()) + { + // The propagating allocator cannot free existing storage. + _Guard __guard(this); + _M_get_allocator() = __str._M_get_allocator(); + this->_M_assign(__str); + __guard._M_release(); + } + else + { + _M_get_allocator() = __str._M_get_allocator(); + this->_M_assign(__str); + } + } +#endif + template<typename _CharT, typename _Traits, typename _Alloc> void basic_string<_CharT, _Traits, _Alloc>:: |