I'm implementing a MinMaxStack<T> in C++20 that tracks minimum and maximum values. The class needs T to support <, >, and == operators.
Could you please do a code review for this?
I have two questions regarding requires in the code:
I'm unsure whether to use a standard concept or to define a custom concept with minimal requirements. If you have better/simpler ideas please let me know.
Is
requires std::constructible_from<T, U>accurate for the methodpush()?
Option 1: Standard concept on class
#include <concepts>
#include <stack>
template <typename T>
requires std::totally_ordered<T>
class MinMaxStack {
public:
using Stack = std::stack<T>;
using size_type = typename Stack::size_type;
using value_type = T;
template <typename U>
requires std::constructible_from<T, U>
void push(U&& value) {
T v{std::forward<U>(value)};
const bool isNewMin = m_mins.empty() || v <= m_mins.top();
const bool isNewMax = m_maxs.empty() || v >= m_maxs.top();
m_data.push(std::move(v));
if (isNewMin) m_mins.push(m_data.top());
if (isNewMax) m_maxs.push(m_data.top());
}
void pop() {
if (empty()) {
return;
}
const auto& top = m_data.top();
const bool isMin = (top == m_mins.top());
const bool isMax = (top == m_maxs.top());
m_data.pop();
if (isMin) m_mins.pop();
if (isMax) m_maxs.pop();
}
[[nodiscard]] const T* top() const noexcept {
if (empty()) {
return nullptr;
}
return &m_data.top();
}
[[nodiscard]] const T* getMin() const noexcept {
if (empty()) {
return nullptr;
}
return &m_mins.top();
}
[[nodiscard]] const T* getMax() const noexcept {
if (empty()) {
return nullptr;
}
return &m_maxs.top();
}
[[nodiscard]] size_type size() const noexcept {
return m_data.size();
}
[[nodiscard]] bool empty() const noexcept {
return m_data.empty();
}
private:
Stack m_data;
Stack m_mins;
Stack m_maxs;
};
Option 2: Custom minimal concept
#include <concepts>
#include <stack>
template <typename T>
concept MinMaxComparable = requires(const T& a, const T& b) {
{ a < b } -> std::convertible_to<bool>;
{ a > b } -> std::convertible_to<bool>;
{ a == b } -> std::convertible_to<bool>;
};
template <typename T>
requires MinMaxComparable<T>
class MinMaxStack {
public:
template <typename U>
requires std::constructible_from<T, U>
void push(U&& value);
// ... same implementation
};