Title
Unintended destruction ordering-specification of resize
Status
c++17
Section
[vector.capacity]
Submitter
Daniel Krügler

Created on 2012-06-07.00:00:00 last changed 90 months ago

Messages

Date: 2014-11-08.16:43:57

Proposed resolution:

This wording is relative to N3936.

  1. Change [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 sequenceequivalent to calling pop_back() size() - sz times. OtherwiseIf 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 sequenceequivalent to calling pop_back() size() - sz times. OtherwiseIf size() < sz, appends sz - size() copies of c to the sequence.

    […]

  2. Change [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 sequenceequivalent to calling pop_back() size() - sz times. OtherwiseIf 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 sequenceequivalent to calling pop_back() size() - sz times. OtherwiseIf size() < sz, appends sz - size() copies of c to the sequence.

    […]

Date: 2014-11-08.16:43:57

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

Date: 2014-06-28.00:00:00

[ 2014-06-28 Daniel provides alternative wording ]

Date: 2014-06-28.22:14:01

[ 2014-06 Rapperswil ]

Daniel points out that the ordering change was not intended.

General agreement that implementations should not be required to change.

Date: 2013-03-15.00:00:00

[ 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.

Date: 2012-06-07.00:00:00

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 [deque.capacity] and [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.

History
Date User Action Args
2017-07-30 20:15:43adminsetstatus: wp -> c++17
2015-05-22 18:31:21adminsetstatus: ready -> wp
2014-11-08 16:43:57adminsetmessages: + msg7169
2014-11-08 16:43:57adminsetstatus: review -> ready
2014-06-28 22:20:47adminsetstatus: open -> review
2014-06-28 22:14:01adminsetmessages: + msg7079
2014-06-28 22:14:01adminsetmessages: + msg7078
2014-06-28 22:14:01adminsetmessages: + msg7077
2013-03-18 14:33:00adminsetmessages: + msg6411
2013-03-18 13:02:36adminsetstatus: new -> open
2012-06-07 00:00:00admincreate