With --enable-fully-dynamic-string the COW basic_string move constructor is noexcept(false), because it has to allocate a new empty rep for the moved-from string.
If we did this, it would only throw when the string we're moving from is "leaked" (that is, there are potentially pointers, references or iterators into the string that prevent it being shared): --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -3575,7 +3575,10 @@ _GLIBCXX_END_NAMESPACE_CXX11 #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0 __str._M_data(_S_empty_rep()._M_refdata()); #else - __str._M_data(_S_construct(size_type(), _CharT(), get_allocator())); + if (__str._M_is_leaked()) + __str._M_data(_S_construct(size_type(), _CharT(), get_allocator())); + else + (void) _M_rep()->_M_refcopy(); #endif } If the string is sharable then a move can just increase the refcount, which won't throw. This would break the guarantee that a moved-from string is empty, but the standard doesn't guarantee that anyway, and we don't leave COW strings empty after move assignment. Thoughts?