vector::push_back insists on using copy constructor though a move constructor is provided

前端 未结 2 2112
感情败类
感情败类 2020-12-15 06:08

I was receiving a strange error from gcc and cannot figure out why. I made the following example code to make the problem more clear. Basically, there is a class defined, fo

相关标签:
2条回答
  • 2020-12-15 06:41

    Try adding "noexcept" to the declaration of the move constructor.

    I can't quote the standard, but recent versions of gcc appear to require either that the copy constructor be public or that the move constructor be declared "noexcept". Regardless of the "noexcept" qualifier, if you make the copy constructor public, it will behave as you expect at run-time.

    0 讨论(0)
  • 2020-12-15 06:49

    Unlike suggested by the previous answer, gcc 4.7 was wrong to reject this code, a mistake which has been corrected in gcc 4.8.

    The full standard-conforming behavior for vector<T>::push_back is:

    • If there is only a copy constructor and no move constructor, push_back will copy its argument and will give the strong exception safety guarantee. That is, if the push_back fails due to an exception triggered by reallocation of the vector storage, the original vector will remain unchanged and usable. This is the known behavior from C++98 and it is also the reason for the mess that follows.
    • If there is a noexcept move constructor for T, push_back will move from its argument and will give the strong exception guarantee. No surprises here.
    • If there is a move constructor that is not noexcept and there is also a copy constructor, push_back will copy the object and give the strong exception safety guarantee. This is unexpected at first glance. While push_back could move here, that would only be possible at the expense of sacrificing the strong exception guarantee. If you ported code from C++98 to C++11 and your type is movable, that would silently change the behavior of existing push_back calls. To avoid this pitfall and maintain compatibility with C++98 code, C++11 falls back to the slower copy. This is what the gcc 4.7 behavior is all about. But there is more...
    • If there is a move constructor that is not noexcept but no copy constructor at all - that is, the element can only be moved and not copied - push_back will perform the move but will not give the strong exception safety guarantee. This is where gcc 4.7 went wrong. In C++98 there are no push_backs for types that are movable but not copyable. So sacrificing the strong exception safety here does not break existing code. This is why it is allowed and the original code is in fact legal C++11.

    See cppreference.com on push_back:

    If an exception is thrown, this function has no effect (strong exception guarantee).

    If T's move constructor is not noexcept and the copy constructor is not accessible, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.

    Or a little more convoluted §23.3.6.5 from the C++11 Standard (emphasis added by me):

    Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.

    Or if you don't like reading, Scott Meyer's Going Native 2013 talk (starting at 0:30:20 with the interesting part at about 0:42:00).

    0 讨论(0)
提交回复
热议问题