[Bug libstdc++/83981] New: vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 Bug ID: 83981 Summary: vector::resize(size_type) should not require T to be CopyInsertable when std=c++14 Product: gcc Version: 7.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: dtrebbien at gmail dot com Target Milestone: --- In C++14, the requirements of vector::resize(size_type) changed. Whereas C++11 requires T to be CopyInsertable, C++14 only requires T to be MoveInsertable and DefaultInsertable. Prompted by investigating the Stack Overflow question "boost::optional vs std::optional for non copyable objects" (https://stackoverflow.com/q/48340618/196844 ), it appears that there are cases where libstdc++ requires T to be CopyInsertable when std=c++14. Here is a test case: #include #include struct foo { foo() {} foo(foo&&) {} }; int main() { typedef boost::optional T; typedef std::allocator A; typedef std::vector X; A m; T *p = std::allocator_traits::allocate(m, 1), v; // DefaultInsertable std::allocator_traits::construct(m, p), std::allocator_traits::destroy(m, p); // MoveInsertable std::allocator_traits::construct(m, p, std::move(v)), std::allocator_traits::destroy(m, p); // CopyInsertable - not well-formed //std::allocator_traits::construct(m, p, v), std::allocator_traits::destroy(m, p); X a; a.resize(1); return 0; } This does not compile in g++ 7.2.0, which produces the error message: "error: use of deleted function 'constexpr foo::foo(const foo&)'". The full error output mentions that __uninitialized_move_if_noexcept_a() is being instantiated, which instantiates __uninitialized_copy_a(), but __uninitialized_copy_a() does not work because T is not copyable.
[Bug libstdc++/83982] New: Exception guarantee of C++14 vector::resize(size_type) is not met
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83982 Bug ID: 83982 Summary: Exception guarantee of C++14 vector::resize(size_type) is not met Product: gcc Version: 7.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: dtrebbien at gmail dot com Target Milestone: --- In [vector.capacity], C++14 mentions for `void resize(size_type sz)': "Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects." Testing g++ 7.2.0, I have found that if an exception is raised by the default constructor, then there might be effects. Here is a test case: #include #include #include #include struct T { static int s_count; T() : m_num(s_count++) { if (s_count > 1) { throw std::runtime_error("too many"); } } T(T&& other) : m_num(other.m_num) { other.m_num = -1; } int m_num; }; int T::s_count = 0; int main() { std::vector a(1); std::cout << "a[0].m_num before = " << a[0].m_num << '\n'; assert(a[0].m_num == 0); try { a.resize(2); } catch (...) { // ignore } std::cout << "a[0].m_num after = " << a[0].m_num << '\n'; assert(a[0].m_num == 0); return 0; } I expect that a[0].m_num will be unchanged by the failed resize() call; however, it changes to -1.
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #3 from Daniel Trebbien --- I would like to make a patch for this and PR 83982 if that's okay.
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #5 from Daniel Trebbien --- I have run into a small issue. C++11 apparently does not provide an exception guarantee on vector::resize(size_type), whereas in C++14, the exception guarantee is "If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects." Given that C++11 does not provide a guarantee, I was thinking that it would be fine to implement C++14's guarantee when compiling as C++11. However, there are several questions that I have seen on Stack Overflow alone which mention or imply that vector::resize falls back on copying existing elements if the _Tp move constructor is not noexcept: https://stackoverflow.com/questions/6011428/move-constructors-and-the-strong-exception-guarantee https://stackoverflow.com/questions/21592898/c-11-move-semantics-and-stl-containers/ https://stackoverflow.com/questions/28627348/noexcept-and-copy-move-constructors/ https://stackoverflow.com/questions/47604029/move-constructors-of-stl-containers-in-msvc-2017-are-not-marked-as-noexcept/ So, my question is, if __cplusplus is 201103L (C++11 mode), should the call to std::__uninitialized_move_if_noexcept_a be retained, thus providing a strong exception guarantee along the lines of "If an exception is thrown there are no effects.", and preserving backward-compatibility. Or, is it okay to implement C++14's exception guarantee, which is not as strong, in C++11 mode as well?
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #6 from Daniel Trebbien --- Created attachment 43243 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=43243&action=edit Patch for PR 83981 and PR 83982
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #7 from Daniel Trebbien --- So as not to break testsuite/23_containers/vector/capacity/resize/moveable2.cc for C++11, I decided that it would be best to keep the use of std::__uninitialized_move_if_noexcept_a() when compiling as C++11. Please see my proposed patch.
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #12 from Daniel Trebbien --- https://wg21.link/lwg2158 looks relevant, particularly this part: "This requirement is not sufficient if an implementation is free to select copy constructor when !is_nothrow_move_constructible::value && is_copy_constructible::value evaluates to true. Unfortunately, is_copy_constructible cannot reliably determine whether T is really copy-constructible. A class may contain public non-deleted copy constructor whose definition does not exist or cannot be instantiated successfully (e.g., std::vector> has copy constructor, but this type is not copy-constructible)." Even though is_copy_constructible for std::vector> is true, resizing a vector of this element type works because the std::vector move constructor is noexcept, so the move constructor is selected. Jonathan, I personally think that your argument in Comment 10 is persuasive. Further, I like the suggestion in LWG 2158 to add "if !is_nothrow_move_constructible::value && is_copy_constructible::value then T shall be CopyInsertable into *this;" to the requirements of vector::resize(size_type). I think that this would be required because, based on my reading of [temp.inst], it is a well-formed program to instantiate boost::optional, as this does not require the instantiation of the boost::optional copy constructor.
[Bug libstdc++/83982] Exception guarantee of C++14 vector::resize(size_type) is not met
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83982 --- Comment #2 from Daniel Trebbien --- Created attachment 43247 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=43247&action=edit Patch for PR 83982 alone
[Bug libstdc++/83981] vector::resize(size_type) should not require T to be CopyInsertable when std=c++14
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83981 --- Comment #13 from Daniel Trebbien --- (In reply to Jonathan Wakely from comment #9) > Also, if boost::optional had a noexcept move constructor it would work fine. > This is a boost bug. > > The part of the patch addressing PR 83982 seems right. I have split out the part of the patch addressing PR 83982 and have attached the reduced patch there.
[Bug libstdc++/83982] Exception guarantee of C++14 vector::resize(size_type) is not met
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83982 --- Comment #3 from Daniel Trebbien --- Friendly reminder: I have attached a patch for this issue which I would like to have reviewed and committed.
[Bug libstdc++/83982] Exception guarantee of C++14 vector::resize(size_type) is not met
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83982 --- Comment #5 from Daniel Trebbien --- Ah! Thank you for letting me know.