C++ 如何在一个集合中插入一个新值,同时删除另一个值?

C++ 如何在一个集合中插入一个新值,同时删除另一个值?,c++,c++11,stl,set,C++,C++11,Stl,Set,每个集合包含按指定顺序排列的元素。我想在集合的大小上指定一个界限,并在插入一个严格更小的新元素(按顺序)并且已经达到指定大小时自动删除最后一个元素 当然,我可以这样做: class bounded_set { private: using set = std::set<Key, Compare, Allocator>; using iterator = typename set::iterator; public: bounded_set(std::size

每个集合包含按指定顺序排列的元素。我想在集合的大小上指定一个界限,并在插入一个严格更小的新元素(按顺序)并且已经达到指定大小时自动删除最后一个元素

当然,我可以这样做:

class bounded_set
{
private:
    using set = std::set<Key, Compare, Allocator>;
    using iterator = typename set::iterator;

public:
    bounded_set(std::size_t size)
        : m_size(size)
    { }

    std::pair<iterator, bool> insert(Key const& value)
    {
        if (m_set.size() < m_size)
            return m_set.insert(value);

        auto last = std::prev(m_set.end());
        if (Compare()(value, *last))
        {
            m_set.erase(last);
            return m_set.insert(value);
        }
        return std::make_pair(last, false);
    }

private:
    set m_set;
    std::size_t m_size;
};
类有界集合
{
私人:
使用set=std::set;
使用iterator=typename set::iterator;
公众:
有界集合(标准::大小\u t大小)
:m_尺寸(尺寸)
{ }
标准::对插入(密钥常量和值)
{
if(m_set.size()
除此之外,
bounded\u set
并不是最好的名称(因为在并发编程世界中,有界容器是众所周知的事情),我担心这个实现中的内存分配。最有可能的是,在开始时,
last
使用的空间将被释放。但紧接着,需要为
value
分配新内存


我真正想做的是,使用为
last
分配的内存,将
value
的数据复制到此位置,同时保留顺序。

如果我正确理解了您的问题,这取决于底层数据结构的工作方式,如果您不必编写自定义内存分配器或使用库中的内存分配器,这不一定是可能的。例如,
std::set
使用红黑树作为底层数据结构。因此,节点的内存位置以及与这些节点之间的关系指针本质上与树的总顺序相关联。如果不重新排序指向该节点的所有指针,使其位于树中该节点的值的正确位置,则不能重用来自“最小”值节点的内存,并将另一个不是新的完全有序“最小”值的值放在该节点上


如果您仍然关心内存使用,并且希望坚持使用STL,而不是
std::set
,也许您应该研究一个固定长度的优先级队列或类似的东西,它使用基于数组的堆作为底层数据结构,这样就不会不断地为新节点分配和重新分配内存。

我为您提供了一些选项,标准委员会失去了一个很容易解决你问题的机会

std::pair<iterator, bool> insert(Key const& value)
{
    if (m_set.size() < m_size)
        return m_set.insert(value);

    auto last = std::prev(m_set.end());
    if (Compare()(value, *last))
    {
        auto temp = m_set.remove(last);
        *temp = value;
        return m_set.insert(temp);
    }
    return std::make_pair(last, false);
}
为你的问题提出了一个解决方案

std::pair<iterator, bool> insert(Key const& value)
{
    if (m_set.size() < m_size)
        return m_set.insert(value);

    auto last = std::prev(m_set.end());
    if (Compare()(value, *last))
    {
        auto temp = m_set.remove(last);
        *temp = value;
        return m_set.insert(temp);
    }
    return std::make_pair(last, false);
}
但是在堆中搜索新插入的值是很困难的,而且
push\u heap
没有提供这些信息

还有一个选项是排序向量+插入排序。您必须自己编写插入排序,但这是一项相对次要的编程任务。需要插入排序的原因是,除了最后一个元素之外,您将始终对已排序的数组进行排序。插入排序是此作业的最佳选择


这些解决方案中没有一个是完美的,只有提供“开箱即用”的解决方案,即不需要超过几行代码的解决方案。而且根本不存在。如果你认为它应该存在,联系你的C++国家身体代表,并告诉他们。或者自己参与到C++委员会中,然后游说它。

-1,因为整个答案表明重用内存是不可能的,但实际上它可以用定制的分配器来完成。是的,自定义内存分配器可以做到这一点,我已经改变了我的答案来反映它。但是,当STL有其他更容易获得、测试的工具时,它似乎不必要地复杂。。。没有?MSVC和boost都有很多分配器。GCC可能也是如此。同时,使用STL实现这一点需要一种奇怪的vector+sort组合。基于数组的堆可能是可以接受的,这取决于他为什么说他需要特定的排序。无论哪种方式,移动/复制的次数都会比
集合
多得多。你可以用向量替换集合。除非您的数据集非常庞大,并且密钥具有昂贵的副本而无需廉价移动,否则性能不会受到影响,而且实际上可能会更快,因为向量的动态内存分配少于集合。如果对向量进行排序,可以在其上使用
std::lower_bound
进行日志(n)搜索。当然,在插入等之前,您需要检查vector是否已经保存了该值。将这些内容包装在一个“flat_set”类中。为什么这样的建议会被拒绝,这有点令人难以置信:对我来说,这似乎完全没有侵扰性。委员会给出了什么理由?@TemplateRex:有人担心如何在不调用未定义行为的情况下以可移植的方式实现它。就我而言,这正是你把它放在标准图书馆的原因。lib实现者编写不可移植的代码,这样其余的代码就不必(至少不必那么多)。但是委员会是由许多不同的人组成的,他们有许多不同的想法。甚至很难将最好的想法标准化。移动语义学花了十年的时间,从来都不是没有争议的。标准化node_ptr
的关键在于有一个有精力和毅力的人来驱动它。