This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++14 status.

2033. Preconditions of reserve, shrink_to_fit, and resize functions

Section: 24.3.11.3 [vector.capacity], 24.3.8.3 [deque.capacity] Status: C++14 Submitter: Nikolay Ivchenkov Opened: 2011-02-20 Last modified: 2016-01-28

Priority: Not Prioritized

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with C++14 status.

Discussion:

I have several questions with regard to the working paper N3225 (C++0x working draft):

  1. Where the working draft specifies preconditions for shrink_to_fit member function of std::vector and std::deque?

  2. Where the working draft specifies preconditions for 'void reserve(size_type n)' member function of std::vector?

  3. Does a call to 'void resize(size_type sz)' of std::vector require the element type to be DefaultConstructible? If yes, why such requirement is not listed in the Requires paragraph?

  4. Does a call to 'void resize(size_type sz)' of std::vector require the element type to be MoveAssignable because the call erase(begin() + sz, end()) mentioned in the Effects paragraph would require the element type to be MoveAssignable?

  5. Why CopyInsertable requirement is used for 'void resize(size_type sz)' of std::vector instead of MoveInsertable requirement?

[2011-06-12: Daniel comments and provides wording]

According to my understanding of the mental model of vector (and to some parts for deque) the some requirements are missing in the standard as response to above questions:

  1. The preconditions of shrink_to_fit for both std::vector and std::deque should impose the MoveInsertable requirements. The reason for this is, that these containers can host move-only types. For a container type X the C++03 idiom X(*this).swap(*this) imposes the CopyInsertable requirements which would make the function call ill-formed, which looks like an unacceptable restriction to me. Assuming the committee decides to support the move-only case, further wording has to be added for the situation where such a move-only type could throw an exception, because this can leave the object in an unspecified state. This seems consistent with the requirements of reserve, which seems like a very similar function to me (for vector). And this brings us smoothly to the following bullet:
  2. I agree that we are currently missing to specify the preconditions of the reserve function. My interpretation of the mental model of this function is that it should work for move-only types, which seems to be supported by the wording used in 24.3.11.3 [vector.capacity] p2:

    […] If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.

    Given this statement, the appropriate requirement is MoveInsertable into the vector.

  3. I agree that vector::resize(size_type) misses to list the DefaultConstructible requirements.
  4. Unfortunately the current specification in terms of erase implies the MoveAssignable requirements. I don't think that this implication is intended. This function requires "append" and "pop-back" effects, respectively, where the former can be realized in terms of MoveInsertable requirements. The same fix in regard to using pop_back instead of erase is necessary for the two argument overload of resize as well (no MoveAssignable is required).
  5. The CopyInsertable requirement is incorrect and should be MoveInsertable instead.

In addition to above mentioned items, the proposed resolution adds a linear complexity bound for shrink_to_fit and attempts to resolve the related issue 2066.

[ 2011 Bloomington ]

Move to Ready.

Note for editor: we do not normally refer to 'linear time' for complexity requirements, but there is agreement that any clean-up of such wording is editorial.

Proposed resolution:

This wording is relative to the FDIS.

  1. Edit 24.3.8.3 [deque.capacity] as indicated [Remark: The suggested change of p4 is not redundant, because CopyInsertable is not necessarily a refinement of MoveInsertable in contrast to the fact that CopyConstructible is a refinement of MoveConstructible]:

    void resize(size_type sz);
    

    -1- Effects: If sz <= size(), equivalent to erase(begin() + sz, end());calling pop_back() size() - sz times. If size() < sz, appends sz - size() value-initialized elements to the sequence.

    -2- Requires: T shall be MoveInsertable into *this and DefaultConstructible.

    void resize(size_type sz, const T& c);
    

    -3- Effects: If sz <= size(), equivalent to calling pop_back() size() - sz times. If size() < sz, appends sz - size() copies of c to the sequence.

    if (sz > size())
      insert(end(), sz-size(), c);
    else if (sz < size())
      erase(begin()+sz, end());
    else
      ; // do nothing
    

    -4- Requires: T shall be MoveInsertable into *this and CopyInsertable into *this.

    void shrink_to_fit();
    

    -?- Requires: T shall be MoveInsertable into *this.

    -?- Complexity: Takes at most linear time in the size of the sequence.

    -5- Remarks: shrink_to_fit is a non-binding request to reduce memory use but does not change the size of the sequence. [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ]

  2. Edit 24.3.11.3 [vector.capacity] as indicated including edits that also resolve 2066 [Remark: The combined listing of MoveInsertable and CopyInsertable before p12 is not redundant, because CopyInsertable is not necessarily a refinement of MoveInsertable in contrast to the fact that CopyConstructible is a refinement of MoveConstructible]:

    […]

    void reserve(size_type n);
    

    -?- Requires: T shall be MoveInsertable into *this.

    -2- Effects: A directive that informs a vector of a planned change in size, so that it can manage the storage allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve(). If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.

    -3- Complexity: It does not change the size of the sequence and takes at most linear time in the size of the sequence.

    -4- Throws: length_error if n > max_size().[footnote 266]

    -5- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity().

    void shrink_to_fit();
    

    -?- Requires: T shall be MoveInsertable into *this.

    -?- Complexity: Takes at most linear time in the size of the sequence.

    -6- Remarks: shrink_to_fit is a non-binding request to reduce capacity() to size(). [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.

    […]

    void resize(size_type sz);
    

    -9- Effects: If sz <= size(), equivalent to erase(begin() + sz, end());calling pop_back() size() - sz times. If size() < sz, appends sz - size() value-initialized elements to the sequence.

    -10- Requires: T shall be CopyMoveInsertable into *this and DefaultConstructible.

    -??- Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.

    void resize(size_type sz, const T& c);
    

    -11- Effects: If sz <= size(), equivalent to calling pop_back() size() - sz times. If size() < sz, appends sz - size() copies of c to the sequence.

    if (sz > size())
      insert(end(), sz-size(), c);
    else if (sz < size())
      erase(begin()+sz, end());
    else
      ; // do nothing
    

    -??- Requires: T shall be MoveInsertable into *this and CopyInsertable into *this.

    -12- RequiresRemarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.