【问题标题】:C++ Compare stacks of shared pointersC++ 比较共享指针堆栈
【发布时间】:2019-02-19 05:37:06
【问题描述】:

假设我有一个堆栈,其中包含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 指向的实际值而不是指针本身?

【问题讨论】:

  • 你没有。对堆栈的访问仅限于 push 和 pop。如果您需要比较相等性,则需要使用向量。
  • stack 的相关性如何?试试bool b = make_shared&lt;int&gt;(1) == make_shared&lt;int&gt;(1);
  • 您是否尝试比较整个堆栈并验证指针引用的所有值在两个堆栈上是否相同?或者您只是想验证每个堆栈的顶部是否具有同等价值?
  • 我正在尝试比较整个堆栈并验证指针引用的所有值

标签: c++ pointers shared-ptr comparator


【解决方案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添加声明是在邀请鼻恶魔栖息。特别是当这些已经定义时。我最初投了反对票,但最后一句话是赎回。只需删除第一个不好的建议即可。
  • 我同意这是一个糟糕的解决方案。
  • 那为什么要提供呢? Stack Overflow 旨在成为一个有价值的知识库。
  • 放下枪,伙计们,我已经纠正了我糟糕的解决方案
  • 你正式赢得了我的支持。您最初的解决方案让我想投反对票,但最后的句子让我无法触发。
【解决方案2】:

我喜欢@RealFresh's answer。它准确地展示了“封装”受保护的可访问性到底是如何的。但是,将基类子对象引用转换为派生类子对象引用,然后将其视为一个引用,很快就会导致未定义的行为。

然而,提取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;
} 

由于表达式&amp;extractor::cworks,我们实际上获得了一个指向basestd::stack 特化)成员的指针,名为cextractor 的目的是通过 using 声明使名称可公开访问。

然后我们转发一个对它的引用,保留值类别和所有。在 @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);
    }
); 

See it live

【讨论】:

  • 非常聪明,但这肯定是UB,不是吗?
  • @RichardHodges - 不。 extractor 使名称 c 可访问。然后形成指向成员&amp;extractor::c 的指针实际上给出了C stack&lt;T, C&gt;::* 类型。这一切都在链接的标准段落中。
  • 同意。只是显示 - protected 真的意味着 public 并且永远不应该使用。
  • @RichardHodges - 确实!
猜你喜欢
  • 1970-01-01
  • 2022-01-23
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-04
  • 2013-01-20
  • 2012-07-01
相关资源
最近更新 更多