C++ C++;std::set更新很乏味:我可以';不要在适当的位置更改元素

C++ C++;std::set更新很乏味:我可以';不要在适当的位置更改元素,c++,stl,set,C++,Stl,Set,我发现std::set上的更新操作很乏味,因为在上没有这样的API。所以我现在做的是这样的: //find element in set by iterator Element copy = *iterator; ... // update member value on copy, varies Set.erase(iterator); Set.insert(copy); 基本上,Set返回的迭代器是一个常量迭代器,不能直接更改其值 有更好的方法吗?或者我应该通过创建自己的(我不知道它是如何

我发现
std::set
上的更新操作很乏味,因为在上没有这样的API。所以我现在做的是这样的:

//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
基本上,
Set
返回的迭代器是一个
常量迭代器
,不能直接更改其值


有更好的方法吗?或者我应该通过创建自己的(我不知道它是如何工作的)来覆盖
std::set

set
返回
const\u迭代器
(标准规定,
set::iterator
const
,而
set::const\u iterator
set::iterator
实际上可能是同一类型-参见n3000.pdf中的23.2.4/6)因为它是一个已排序的容器。如果它返回一个常规的
迭代器
,则允许您更改容器下的items值,这可能会改变排序


您的解决方案是以惯用的方式更改
集合中的项目。您可能需要使用
std::map
。使用
元素中影响键排序的部分,并将所有
元素
作为值。会有一些较小的数据重复,但您会更轻松(可能更快)更新。

更新:尽管以下情况到目前为止都是正确的,但这种行为被认为是错误的,并将在即将发布的标准版本中进行更改。这太令人伤心了


有几点让你的问题相当混乱

  • 函数可以返回值,类不能返回值。
    std::set
    是一个类,因此不能返回任何内容
  • 如果可以调用
    s.erase(iter)
    ,则
    iter
    不是一个
    const\u迭代器
    erase
    需要一个非const迭代器
  • 返回迭代器的
    std::set
    的所有成员函数都返回一个非常量迭代器,只要该集合也是非常量的
  • 只要更新不改变元素的顺序,就可以更改集合中元素的值

    #include <set>
    
    int main()
    {
        std::set<int> s;
        s.insert(10);
        s.insert(20);
    
        std::set<int>::iterator iter = s.find(20);
    
        // OK
        *iter = 30;
    
        // error, the following changes the order of elements
        // *iter = 0;
    }
    
    #包括
    int main()
    {
    std::集s;
    s、 插入(10);
    s、 插入(20);
    std::set::迭代器iter=s.find(20);
    //嗯
    *iter=30;
    //错误,以下更改元素的顺序
    //*iter=0;
    }
    

    如果更新更改了元素的顺序,则必须擦除并重新插入。

    在简单的情况下,有两种方法可以做到这一点:

    • 您可以对不属于键的变量使用
      mutable
    • 您可以将类拆分为
      对(并使用
      std::map

    现在,问题是棘手的情况:当更新实际修改对象的
    部分时会发生什么?您的方法很有效,尽管我承认它很乏味。

    在某些情况下,这会更快:

    std::pair<std::set<int>::iterator, bool> result = Set.insert(value);
    if (!result.second) {
      Set.erase(result.first);
      Set.insert(value);
    }
    
    std::pair result=Set.insert(值);
    如果(!result.second){
    设置。擦除(结果。第一);
    设置。插入(值);
    }
    

    如果值通常不在
    std::set
    中,那么这可能会有更好的性能。

    我在C++11中遇到了同样的问题,在C++11中,
    ::std::set::iterator
    确实是常量,因此不允许更改其内容,即使我们知道转换不会影响
    data=data;}
    运算符T&()常量{返回数据;}
    T*运算符->()常量{return&data;}
    
    friend bool运算符在C++17中,由于以下原因,您可以使用它做得更好:

    这将避免额外的类型副本和额外的分配/解除分配,并且也适用于仅移动类型

    您还可以按键
    提取
    。如果缺少键,这将返回一个空节点:

    auto node = Set.extract(key);
    if (node) // alternatively, !node.empty()
    {
        node.value() = 42;
        Set.insert(std::move(node));
    }
    

    如果你发现使用2条语句已经很乏味,那么就创建一个内联函数。KennyTM一针见血。这样做没有任何性能方面的负面影响,所以现在就做吧!:-PIf如果你编写一个更新函数,你可能会想用与Boost相同的方式对其建模。MultiIndex:cplusplus.com是一个糟糕的参考。查找语言的方面“冗长”是因为它看起来…奇怪。我认为这种方法的缺点是在并发处理的情况下使用读/写锁。(非常量)
    std::set
    的成员不返回
    const\u迭代器
    ,如果你小心的话,你可以修改它的元素。为什么(不正确)答案得到这么多的支持票?我遗漏了什么?我的答案是正确的-你的答案是错误的。我更新了我的帖子,引用了标准。Terry,谢谢你的讨论。我重新检查了一下:缺陷报告确实是在1998年提交的,但没有被纳入C++03。它将被纳入C++0x。因此,尽管你的答案到目前为止并不正确就目前的标准而言,就其意图而言,它是正确的。+1.感谢您的评论以及您与Avakar(或Avatar?)的辩论。他们帮助了很多。@Avakar:元素在技术上是可变的,但不允许更改。这是标准中的一个缺陷。好吧,我在看23.1.2[lib.associative.reqmts]关于C++03,表69,上面写着:“a.find(k):迭代器;常量a的常量迭代器”。我手头没有C++03 pdf(它需要钱)。我认为这在C++03中是固定的,但它可能是03后的修复。在n3000.pdf中,它是23.2.4/6,这解释了对于关联容器,迭代器是常量迭代器(可以是与const_迭代器相同的类型)。您使用的编译器是什么?此行为是imp
    // remove element from the set, but without needing
    // to copy it or deallocate it
    auto node = Set.extract(iterator);
    // make changes to the value in place
    node.value() = 42;
    // reinsert it into the set, but again without needing 
    // to copy or allocate
    Set.insert(std::move(node));
    
    auto node = Set.extract(key);
    if (node) // alternatively, !node.empty()
    {
        node.value() = 42;
        Set.insert(std::move(node));
    }