5

Is there any guarantee regarding the destruction order of std::stack elements?

I have a class that manages the lifetime of a set of services. Since there may be service interdependencies, the order of construction and destruction is important -- the services should be destroyed in the reverse order of their creation.

I thought I would use a std::stack<std::unique_ptr<Service>> for this purpose. Knowing that a stack is a container adapter, and guessing that this might affect its destruction semantics, I searched, but I couldn't find any documentation (page 800) that guaranteed the order of destruction for the elements of a std::stack.

In the end, I wrote a little test:

struct Squealer {
    Squealer() {
        static int instance_count = 0;
        this->instance = ++instance_count;
    }
    ~Squealer() {
        std::cout << "Destroying instance " << instance << std::endl;
    }
    int instance;
};

int main(int argc, char *[] argv) {
    {
        std::stack<Squealer> squealers;
        squealers.emplace();
        squealers.emplace();
        squealers.emplace();
    }
    std::cout << "...done" << std::endl;
}

The result was as expected:

Destroying instance 3
Destroying instance 2
Destroying instance 1
...done

Should I be relying on this behavior? Is the naive destruction order guaranteed for std::stack, or should I take the (admittedly easy) step of popping it until it is clear?

Justin
  • 655
  • 8
  • 17
  • related: http://stackoverflow.com/questions/2083603/stl-containers-element-destruction-order This is about `vector`, `list` and `map` but I would guess that the same applies for `stack` – 463035818_is_not_an_ai Feb 18 '17 at 18:46
  • 1
    I saw that, but since `stack` is an adapter, I wondered if it would define additional behavior...not that I could find any documentation of that. If both `queue` and `stack` rely on `deque` and neither specifies element destruction order, the default element destruction order is going to be surprising for one of the two. – Justin Feb 18 '17 at 18:49
  • 1
    an ugly workaround would be to pop all the elements manually. Actually I wouldnt expect this to be much less efficient than calling the `stack` destructor – 463035818_is_not_an_ai Feb 18 '17 at 18:57
  • The destruction order is undefined by the standard. Stack is an adapter for implementation a vector, list or a deque can be used. – Lenz Feb 18 '17 at 19:00
  • @Lenz I'll accept an answer to that effect -- I'm just not familiar enough with the standard to be confident that something is *undefined*, versus the possibility that I just *couldn't find* it. – Justin Feb 18 '17 at 19:04

2 Answers2

6

std::stack isn't a Container, it's a Container Adapter. It takes as its second parameter which Container you actually want to use to store the elements:

template<
    class T,
    class Container = std::deque<T>
> class stack;

The destruction semantics of stack<T> would be those of deque<T>. This, however, doesn't really help you very much, since the destruction order of deque<T> is unspecified by the standard. Indeed, it's not specified for any of the sequence containers.

If destruction order is important, then you should do one of two things: either provide a new container that destroys its elements last to first:

template <class T>
struct my_deque : std::deque<T>
{
    using std::deque<T>::deque;

    ~my_deque() {
        while (!this->empty()) this->pop_back();
    }
};

template <class T>
using my_stack = std::stack<T, my_deque<T>>;

Or provide your own implementation of stack whose destructor pops all the elements:

template <class T, class Container = std::deque<T>>
struct ordered_stack : std::stack<T, Container>
{
    using std::stack<T, Container>::stack;

    ~ordered_stack() {
        while (!this->empty()) {
            this->pop();
        }
    }
};
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Didn't immediately occur to me how easy it would be to enforce a destruction order by simply popping the elements. Simple and elegant solution, thank you! – monkey0506 Apr 11 '19 at 05:22
3

By default a stack is backed by a deque (23.6.6.1)

template <class T, class Container = deque<T>>
class stack {

A deque does not have a defined destruction order.

However, you can implement a simple container that supports the stack, you just need to provide back, push_back, and pop_back.

OmnipotentEntity
  • 16,531
  • 6
  • 62
  • 96