Document No: P0752r0 Date: 2017-10-15 Project: ISO SC22/WG21 C++ Standard Author: Nathan Myers # std::vector Destruction Order C++ has always been a language intended for controlling computing machinery, leaving as little room as possible, underneath, requiring code in a lower-level language. One expression of that intent has been its explicit stack-order construction and destruction of program objects. Struct members are constructed in ascending order, from low address to high, and are destroyed in the opposite, descending, order. Built-in arrays are handled likewise, as is std::array. Function-local variables and function activation frames follow the same discipline. I will call this "natural order". It is specified in the standard, and all implementations observe it. The natural, stack order is far from accidental. Construction order creates an implicit dependency graph in which later-constructed objects may reasonably depend on objects constructed before them, but clearly cannot depend on objects not yet constructed. Respecting that graph at destruction time, by not destroying objects that others are most likely to still depend on, means the programmer will not need to explicitly code a destruction order. Respecting implicit dependency may matter especially when the elements represent entities external to, and not under control of, the program. ### The Problem We have discovered an unfortunate divergence of std::vector element destruction ordering in implementations of the standard library. We find implementations that obey natural order, and others that do not. It appears that all these implementations conform, as they are, to the existing wording, because the standard does not suggest any ordering for either constructing or destroying elements of a vector. Other conforming implementations could both construct and destroy in descending natural order, or construct in descending order and destroy in ascending order, although we do not know of any. We find this lack of specification, and lack of conformance to core language practice, unfortunate. It appears that there is no notable technical advantage to using any of the three possible unnatural orderings; i.e., it appears to be a case of historical accident. (There is some evidence that machines do slightly favor performing memory operations in ascending order, but it is weak.) As accidental as the choice may have been in each case, it would be easy for a program to depend, implicitly, on the implementation order choice, and thus not be portable to a natural-order implementation. It seems likely that more programs would depend on the natural order, and would not be portable to an unnatural-order implementation. Your author has not surveyed how consistently the various implementations have observed their default order. For example, do they destroy elements in response to an exception during construction in the same order as when they destroy the whole vector? How about when erasing a range of elements in the middle? We would like to have consistent, reasoned behavior in all of these cases. ### The Proposal We propose to specify, in C++20, that std::vector performs its construction and destruction in the same order as native structs and arrays, and as std::array. In addition, we propose that, where reasonable, std::vector observe the same order when constructing or destroying sub-ranges of elements. It is clear that there must be exceptions to the general rule, for cases where natural order would be impractical, such as the order of moving elements in response to an insertion. We are confident these cases can all be identified, and specified with equal clarity so that implementations will be uniform in these details as well. (We expect they probably all conform, already, in these cases.) Other aggregate types also impose an order. We would like them to be the same, wherever practical. Clearly, destroying elements of a singly-linked list should continue to be done in list order. A defined order for tree and hash structures would be hard even to express. We propose that these be identified as explicitly not required to conform to any specific rule. ### Backward Compatibility Clearly, some existing programs must depend on the unnatural destruction order performed by the implementation they were originally tested on. Even to determine whether a given program is among those might be prohibitively difficult. Fixing it, if it does, might be harder. We cannot expect implementations to abandon support for these programs and their users. Accommodate users dependent on unnatural destruction order could become the most difficult part of this proposal. It seems certain that, whatever appears in the working paper, each divergent implementation must ship with some way for users who cannot afford to fix their programs to build them as-is. The worst outcome would be for implementators to feel obliged to ignore the standard, and ship a now non-conforming library. Almost as bad would be to continue to have no specified destruction order. It is far from clear, though, that the proposal must, or can, specify the accommodations that currently divergent implementations must make for their users. Each implementation has had to adapt to evolution of the standard, and will need to do so in the future; this case is one of many. The usual approach is to provide a "backward-compatibility" command-line flag that restores the old behavior when building the old program. That seems to the author like it should suffice, but opinions may differ. Where they do, they seem to emphasize that the standard is not the place to list alternative accommodations. That said, a global symbol could be defined to allow a portable library to discover whether it is running in an environment with unnatural vector destruction order. ### Votes This proposal does not have Working Paper text yet. If there is interest, a forthcoming draft will have unsurprising text.