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

### 2160. Unintended destruction ordering-specification of `resize`

Section: 24.3.11.3 [vector.capacity] Status: C++17 Submitter: Daniel Krügler Opened: 2012-06-07 Last modified: 2017-07-30

Priority: 1

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with C++17 status.

Discussion:

As part of resolving LWG issue 2033 a wording change was done for `resize()` to respect the problem mentioned in the question:

> 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`?

The wording change was to replace in 24.3.8.3 [deque.capacity] and 24.3.11.3 [vector.capacity]:

> -1- Effects: If `sz <= size()`, equivalent to `erase(begin() + sz, end())`; […]

by:

> -1- Effects: If `sz <= size()`, equivalent to calling `pop_back() size() - sz` times. […]

The overlooked side-effect of this wording change is that this implies a destruction order of the removed elements to be in reverse order of construction, but the previous version did not impose any specific destruction order due to the way how the semantics of `erase` is specified in Table 100.

Given the program:

>     #include <vector>
>     #include <iostream>

>     struct Probe {
>       int value;
>       Probe() : value(0) {}
>       Probe(int value) : value(value) {}
>       ~Probe() { std::cout << "~Probe() of " << value << std::endl; }
>     };

>     int main() {
>       std::vector<Probe> v;
>       v.push_back(Probe(1));
>       v.push_back(Probe(2));
>       v.push_back(Probe(3));
>       std::cout << "---" << std::endl;
>       v.resize(0);
>     }

the last three lines of the output for every compiler I tested was:

>     ~Probe() of 1
>     ~Probe() of 2
>     ~Probe() of 3

but a conforming implementation would now need to change this order to

>     ~Probe() of 3
>     ~Probe() of 2
>     ~Probe() of 1

This possible stringent interpretation makes sense, because one can argue that sequence containers (or at least `std::vector`) should have the same required destruction order of it's elements, as elements of a C array or controlled by memory deallocated with an array `delete` have. I also learned that libc++ does indeed implement `std::vector::resize` in a way that the second output form is observed.

While I agree that required reverse-destruction would better mimic the natural behaviour of `std::vector` this was not required in C++03 and this request may be too strong. My current suggestion would be to restore the effects of the previous wording in regard to the destruction order, because otherwise several currently existing implementations would be broken just because of this additional requirement.

[2013-03-15 Issues Teleconference]

Moved to Open.

Jonathan says that he believes this is a valid issue.

Walter wonders if this was intended when we made the previous change - if so, this would be NAD.

Jonathan said that Issue 2033 doesn't mention ordering.

Walter then asked if anyone is really unhappy that we're destroying items in reverse order of construction.

Jonathan points out that this conflicts with existing practice (libstc++, but not libc++).

Jonathan asked for clarification as to whether this change was intended by 2033.

[2014-06 Rapperswil]

Daniel points out that the ordering change was not intended.

General agreement that implementations should not be required to change.

[2014-06-28 Daniel provides alternative wording]

[Urbana 2014-11-07: Move to Ready]

Proposed resolution:

This wording is relative to N3936.

1. Change 24.3.8.3 [deque.capacity] as indicated: [Drafting note: The chosen wording form is similar to that for `forward_list`. Note that the existing Requires element already specifies the necessary operational requirements on the value type. — end drafting note]

   >     void resize(size_type sz);

   > > -1- Effects: If `sz <= size()`, erases the last `size() - sz` elements from the sequence equivalent to calling `pop_back() size() - sz` times. Otherwise If `size() <= sz`, appends `sz - size()` default-inserted elements to the sequence.

   > > […]

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

   > > -3- Effects: If `sz <= size()`, erases the last `size() - sz` elements from the sequence equivalent to calling `pop_back() size() - sz` times. Otherwise If `size() < sz`, appends `sz - size()` copies of `c` to the sequence.

   > > […]

2. Change 24.3.11.3 [vector.capacity] as indicated: [Drafting note: See `deque` for the rationale of the used wording. — end drafting note]

   >     void resize(size_type sz);

   > > -12- Effects: If `sz <= size()`, erases the last `size() - sz` elements from the sequence equivalent to calling `pop_back() size() - sz` times. Otherwise If `size() < sz`, appends `sz - size()` default-inserted elements to the sequence.

   > > […]

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

   > > -15- Effects: If `sz <= size()`, erases the last `size() - sz` elements from the sequence equivalent to calling `pop_back() size() - sz` times. Otherwise If `size() < sz`, appends `sz - size()` copies of `c` to the sequence.

   > > […]