Допустим, у меня есть стек, который содержит общие указатели для int, как показано ниже:
#include <stack>
#include <memory>
using namespace std;
int main()
{
stack<shared_ptr<int>> s1;
stack<shared_ptr<int>> s2;
shared_ptr<int> v1 = make_shared<int>(1);
shared_ptr<int> v2 = make_shared<int>(1);
s1.push(v1);
s2.push(v2);
bool areEqual = s1 == s2; // This is false
}
Как заставить стеки сравнивать фактические значения, на которые указывает shared_ptr, а не сами указатели?
Насколько stack актуально? Попробуйте bool b = make_shared<int>(1) == make_shared<int>(1);
Вы пытаетесь сравнить весь стек и убедиться, что все значения, на которые ссылаются указатели, идентичны в обоих стеках? Или вы просто пытаетесь проверить, что вершина каждого стека эквивалентна по стоимости?
Я пытаюсь сравнить весь стек и проверить все значения, на которые ссылаются указатели.





std::stack имеет защищенный член c, который является экземпляром базового типа контейнера. Вы можете создать оболочку стека, которая обращается к этой переменной, а затем сравнивать содержимое базовых контейнеров следующим образом:
#include <iostream>
#include <stack>
#include <memory>
#include <algorithm>
using namespace std;
template<class stack_type>
struct stack_wrapper : stack_type
{
auto begin() const
{
return stack_type::c.begin();
}
auto end() const
{
return stack_type::c.end();
}
};
template<class stack_type>
const stack_wrapper<stack_type> &wrap(const stack_type &stack)
{
return static_cast<const stack_wrapper<stack_type> &>(stack);
}
int main() {
stack<shared_ptr<int>> s1;
stack<shared_ptr<int>> s2;
shared_ptr<int> v1 = make_shared<int>(1);
shared_ptr<int> v2 = make_shared<int>(1);
s1.push(v1);
s2.push(v2);
const auto &s1wrapper = wrap(s1);
const auto &s2wrapper = wrap(s2);
const auto is_equal = std::equal(s1wrapper.begin(),
s1wrapper.end(),
s2wrapper.begin(),
s2wrapper.end(),
[](auto &first, auto &second) {
return first && second && *first == *second;
});
std::cout << is_equal << std::endl;
}
Добавление объявлений в пространство имен std приглашает на насест носачих демонов. Особенно когда они уже определены. Я изначально проголосовал против, но последнее предложение искупает. Просто удалите первое плохое предложение.
Я согласен, что это ужасное решение проблемы.
Тогда зачем предлагать? Переполнение стека предназначен для хранения знаний стоящий.
Снимите пушки, ребята, я исправил свое ужасное решение
Вы официально заслужили мой голос. Ваше оригинальное решение заставило меня проголосовать против, но предложение в конце не давало мне покоя.
возможно, стоит указать, что конструкция оболочки вызывает копирование стека. возможно, было бы лучше, если бы он был назван как-то вроде augmented_stack и использовался в качестве основного контейнера.
@RichardHodges - копии нет. Все приведения и возвращаемые значения включают только ссылки.
@StoryTeller ах да, это хуже, чем я думал
Мне нравится Ответ @RealFresh. Он точно демонстрирует, насколько на самом деле «инкапсулируется» защищенный доступ. Но приведение ссылки подобъекта базового класса к ссылке подобъекта производного класса и последующее обращение с ней как с таковой может быстро привести к неопределенному поведению.
Однако идея извлечения члена c здрава. Мы можем сделать это без риска UB с помощью простой утилиты:
template<class S>
constexpr decltype(auto) stack_c(S&& s) {
using base = std::decay_t<S>;
struct extractor : base {
using base::c;
};
constexpr auto c_ptr = &extractor::c;
return std::forward<S>(s).*c_ptr;
}
Из-за того, что выражение &extractor::cработает, мы фактически получаем указатель на член base (специализацию std::stack) с именем c. Цель extractor — сделать имя общедоступным через объявление использования.
Затем мы возвращаем ссылку на него, ценностная категория сохраняется и все. Это замена @RealFresh, предлагающая использовать std::equal:
bool areEqual = std::equal(
stack_c(s1).begin(), stack_c(s1).end(),
stack_c(s2).begin(), stack_c(s2).end(),
[](auto const& p1, auto const& p2) {
return first && second && (p1 == p2 || *p1 == *p2);
}
);
Очень умно, но ведь это должно быть УБ, не так ли?
@RichardHodges - Нет. extractor делает имя c доступным. Затем формирование указателя на элемент &extractor::c фактически дает тип C stack<T, C>::*. Это все в связанном стандартном абзаце.
Согласованный. Просто показывает - protected действительно означает public и никогда не должен использоваться.
@RichardHodges - Действительно!
вы не знаете. доступ к стеку ограничен push и pop. Если вам нужно сравнить равенство, вам нужно будет использовать вектор.