It appears that there is no container element destruction order specified. N3242 §23.2.1 ¶4 Table 96 Row ‘(&a)->∼X()’ only states:
note: the destructor is applied to every element of a; all the memory is deallocated.
What is perhaps more surprising is that std::vector also does not guarantee element destruction order. This is surprising because the language guarantees that normal c-arrays, and dynamically created (i.e. ‘new’ed) c-arrays elements are destroyed in reverse index order.
N3242 §12.6 ¶3:
the constructor shall be called for each element of the array, following the subscript order; destructors for the array elements are called in reverse order of their construction.
N3242 §5.3.5 ¶6:
… the elements will be destroyed in order of decreasing address, that is, in reverse order of the completion
of their constructor; …
Yet, std::vector, which models a c-array, does not provide this guarantee.
I am not the first to consider this a surprising behavior. Piotr Nycz asked about this on comp.lang.c++.moderated, and later on StackOverflow. ( He got some push-back, and some respondents assumed he wanted destruction to be the reverse of how elements were added; I think it was obvious he meant the reverse of the current ordering of the vector. )
It appears this order is left unspecified to allow implementers freedom and not restrict performance. But if performance were so critical, then why do c-arrays guarantee such order. Is it really possible that std::vector destruction can be faster than that of a c-array?!
Separately, it appears that std::tuple also has no order of construction/destruction guarantees; I would have expected this to act like a simple struct/class, whose member (con/de)structor orders are well defined.
This is all surprising because so much of C++’s object lifetime safety is due to the explicit, deterministic order of construction and destruction of objects: automatic variables are well specified, struct/class members are well defined, and c-arrays are well defined. Yet, all of the containers appear to leave this unspecified.
The end result is, no assumptions about other object lifetimes inside of these containers during destruction should be made. So, if you collect a sequence of objects to destroy in a container, you cannot assume the container’s order (if any) will dictate the destruction order; instead, one should ‘erase’ elements explicitly.