Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::stack element destruction order

Tags:

c++

c++11

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?

like image 344
Justin Avatar asked Feb 18 '17 18:02

Justin


2 Answers

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();
        }
    }
};
like image 195
Barry Avatar answered Sep 24 '22 06:09

Barry


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.

like image 32
OmnipotentEntity Avatar answered Sep 26 '22 06:09

OmnipotentEntity