I had a usecase where I use a stack to process some data. Once processed, I want to output the data as a vector. But since underlying containers in stack are protected, it is now allowed to:
stack<int, vector<int>> st;
// Some stack operations
vector<int> v(move(st));
This entails that the copy must necessarily happen. Is there a way to get around this, without using custom stack? (I want the application to be portable, so no changes to STL lib are good)
I tried compiling the above code, and it obviously threw compile time error as the underlying container is not exposed.
Use helper that exposes the adaptor’s protected c and moves it out
#include <stack>
#include <vector>
#include <iostream>
#include <utility>
template<class T, class C>
C take_container(std::stack<T, C>&& st) {
struct exposed : std::stack<T, C> {
using std::stack<T, C>::c; // expose protected member
explicit exposed(std::stack<T, C>&& s)
: std::stack<T, C>(std::move(s)) {}
};
exposed tmp(std::move(st)); // take ownership of adaptor
return std::move(tmp.c); // move out the underlying container
}
int main() {
std::stack<int, std::vector<int>> st;
for (int x : {1,2,3,4,5}) st.push(x); // bottom..top = 1,2,3,4,5
std::vector<int> v = take_container<int, std::vector<int>>(std::move(st));
std::cout << "vector: ";
for (int x : v) std::cout << x << " ";
std::cout << "\n";
}
https://godbolt.org/z/ohvhq7T78
WHAT THE TRICK DOES
std::stack<T,C> is an adaptor around an underlying container C . The C++ standard mandates that these adaptors store that container in a protected data member named c.
Because it is protected, you cannot access st.c from ordinary code. But a class that derives from std::stack<T,C> may access c. So I make a tiny derived helper type that:
inherits from std::stack<T,C>,
brings c into scope with a using declaration,
move-constructs itself from your std::stack<T,C>,
then it returns std::move(tmp.c); to move the underlying container out.
This gives you the container by move, without copying elements.
CONSTRAINTS AND CAVEATS
Works for any C that is movable (vector, deque, list, etc.). If C is not movable, this will not compile.
Do not use st after calling take_container(std::move(st)). It is moved-from; you may only destroy or assign to it.
This relies on the adaptor’s protected member name c. That is mandated by the standard for adaptor classes and has been stable across C++ versions.
Exception safety: you can make the function noexcept if moving C is noexcept
You might inherit from std::stack to expose the underlying container:
template <typename T>
struct myStack : std::stack<T, std::vector<T>>
{
using std::stack<T, std::vector<T>>::c; // underlying container
// probably better to create dedicated method as extract()
// instead of exposing it directly though
};
and then
myStack<int> st;
// Some stack operations
std::vector<int> v(std::move(st.c));
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With