C++ std :: set update很乏味:我无法更改元素

Fig*_*igo 66 c++ stl set

我发现更新操作std::set很繁琐,因为cppreference上没有这样的API .所以我现在做的是这样的:

//find element in set by iterator
Element copy = *iterator;
... // update member value on copy, varies
Set.erase(iterator);
Set.insert(copy);
Run Code Online (Sandbox Code Playgroud)

基本上迭代器返回的Set是a const_iterator,你不能直接改变它的值.

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

Ter*_*fey 72

set返回const_iterators(标准说set<T>::iteratorconst,这set<T>::const_iteratorset<T>::iterator可能实际上是相同类型的-见23.2.4/6在n3000.pdf),因为它是一个有序的容器.如果它返回常规iterator,则允许您从容器下面更改项值,可能会改变排序.

你的解决方案是改变一个项目的惯用方法set.

  • 特里,谢谢你的讨论.我已经重新检查过:缺陷报告确实是在1998年提交的,但没有被纳入C++ 03.它将进入C++ 0x.因此,尽管就标准的当前字母而言你的答案是不正确的,但就意图而言,它是正确的.+1. (6认同)
  • @avakar:元素在技术上是可变的,但不允许改变它们.这是标准中的缺陷. (2认同)

Mat*_* M. 24

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

  • 您可以使用mutable不属于键的变量
  • 你可以将你的班级分成一Key Value对(并使用一个std::map)

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

  • 对非关键成员+1可变是一个好主意 (4认同)
  • 如果一个内部/私有类,在非关键成员上添加mutable可能没问题,但它仍然是一个肮脏的黑客!一旦该类暴露给某些用户,id就永远不敢对可能不可变的成员使用mutable!那是邪恶的! (3认同)

Bar*_*rry 10

在C++ 17中,你可以做得更好extract(),感谢P0083:

// 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));
Run Code Online (Sandbox Code Playgroud)

这将避免您的类型的额外副本和额外的分配/释放,并且还将适用于仅移动类型.

你也可以extract按键.如果密钥不存在,则返回空节点:

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


小智 8

你可能想用一个std::map代替.使用Element影响键的排序的部分,并将所有Element值作为值.将会有一些小的数据重复,但您将更容易(也可能更快)更新.


ava*_*kar 8

更新:虽然以下情况属于现在,但该行为被视为缺陷,将在即将发布的标准版本中进行更改.多么难过


有几点使您的问题相当混乱.

  1. 函数可以返回值,类不能.std::set是一个类,因此不能返回任何东西.
  2. 如果你可以打电话s.erase(iter),那就iter不是了const_iterator.erase需要一个非const迭代器.
  3. std::set返回迭代器的所有成员函数返回非const迭代器,只要该集合也是非const.

只要更新不更改元素的顺序,就可以更改集合元素的值.以下代码编译并正常工作.

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 它位于标准库缺陷报告中:http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#103.它不能与GCC一起编译,后者提到DR 103和typedef,iterator和const_iterator都是同一类型. - 针对OP问题的一个建议解决方案BTW是const_cast和可变成员. (4认同)
  • 在评论中有趣的讨论+1 (2认同)