- 主题:更新保存在 SET 里的结构体成员
刚才有人说能白嫖gpt4。试了一下,完美回答你的问题
首先,你需要知道std::set是一种关联容器,它能够储存唯一元素。元素是排序的,排序是通过使用其关键的比较函数进行的。
对于你的问题,如果你想要在std::set<Test>中找到特定的元素并且修改它,那从技术上来讲是无法做到的。因为set中的元素默认是const的,这样做是为了防止你修改元素值,从而导致set内元素的顺序破坏。当元素插入set时,set会根据元素值排序,如果你修改了元素,那么set的内部顺序就会被打乱,这是set不允许的。
你可以使用std::set的find函数来寻找特定的元素,但是你不能直接修改它。如果你真的需要修改某个元素,你需要先从set中删除它,然后修改它,最后再重新插入到set中。也可以考虑使用std::map,它允许你使用键值对的形式来存储数据,而且键(在你的案例中就是id)是唯一的。
【 在 WaveWave 的大作中提到: 】
: 见下列代码,虽然可以做到,但是感觉很丑陋,有更好的办法吗?
: [code=c]
: class Test
: ...................
--
FROM 61.48.130.*
iterator就是iterator,并不是const_iterator。
但为啥不能改,chatgpt分析得很详细。可以对照一下STL源码看它说得对不。
Q:
std::set<>只有const_iterator吗?
A:
是的,std::set 只提供了 const iterators, 不提供 non-const iterators。这是因为在 set 中,元素的位置是基于其值的,任何改变元素的操作都可能导致元素的重新排序,这也是为什么 set 中的元素是 const 的原因。
所以,当你需要在 set 中遍历元素时,你应使用 const_iterator. 大致的代码如下:
std::set<int>::const_iterator it;
for (it = myset.begin(); it != myset.end(); ++it)
{
std::cout << ' ' << *it;
}
这段代码将遍历 set 中的每一个元素,并将它们打印到标准输出。注意,你不能使用这个 iterator 来改变元素的值,因为set中元素是只读的。如果你试图通过 iterator 修改元素的值,编译器将返回错误。
Q:
但是我看MSVC的STL源码中,std::set<>的find()有两种重载格式,一种返回iterator,另一种返回const_iterator。怎么解释呢?
A:
你的观察是准确的。在MSVC的STL实现中,的确有两种重载版本的std::set<>::find,一种返回iterator,而另一种返回const_iterator。
不过这并不表示你可以通过这些iterator修改元素的值。要注意,虽然形式上返回的是iterator,实际上这些iterator是只读的,也就是说,你无法使用它们来修改元素的值。
这实际上是为了支持常量和非常量版本的成员函数。通常来说,对于那些并未修改数据成员的成员函数,我们会提供一个常量版本和一个非常量版本。非常量版本返回非常量迭代器,而常量版本返回常量迭代器。这样使得在需要返回迭代器的情况下,常量版本的成员函数可以被编译器选中。
总的来说,std::set中的iterator其实是const_iterator。即使find方法返回的是iterator类型,你依然无法用返回的迭代器修改集合内的元素。
Q:
返回的是iterator而不是const_iterator,但依然无法修改iterator指向的数据,请问这是怎么做到的?很好奇其实现细节。
A:
这个问题涉及到C++的内部实现细节。在MSVC的STL实现中,std::set中的迭代器是一种被称为_Tree::iterator的类型,其中的_Tree是set的底层数据结构——一个红黑树。在MSVC的STL实现中,红黑树迭代器的操作符*重载返回的是const类型的引用,如下所示:
const _Ty& operator*() const
{ // return designated object
return (_Mynode()->_Myval);
}
这就是为什么尽管find函数返回iterator,但是你不能通过这个迭代器修改指向元素的值的原因。operator*()返回的是一个const引用,你不能通过这个const引用去修改元素的值。尝试这么做的话,编译器会抛出错误。
这个做法很聪明,因为它允许库的设计者通过在底层实现中施加const来保护容器的元素,同时在API级别呈现出非const迭代器,这对许多代码(如算法)是必要的,它们需要以相同的方式处理const和非const迭代器,即使那些迭代器指向的元素不能被修改。
Q:
如果我不通过operator*()来修改元素,而是用operator->()来修改呢?
A:
在大多数情况下,operator*() 和 operator->() 的作用是相似的, 它们都是用于访问被迭代器指向的元素。当你的迭代器是指向类或结构体的指针时,你可能会需要使用 operator->() 去访问这些结构体或类的成员。
但对于 set 的迭代器来说, 使用 operator->() 来修改元素的值也是不允许的。这是因为无论是 operator*() 还是 operator->(),它们返回的都是对集合元素的 const 引用,这意味着你不能用它们来修改元素的值。如果你尝试这样做, 编译器会抛出错误。
为了证明这一点, 你可以看一下 MSVC STL 中对 set iterator 的 _Ref 类型的定义:
typedef const _Ty& _Ref;
typedef const _Ty * _Ptr;
这意味着 operator->() 返回的其实是一个指向 const 值的指针,因此还是不能用来修改元素的值。这就是 MSVC STL 实现中为何只能用 set 的迭代器来读取值,而不能修改值的原因。
【 在 gameplayer 的大作中提到: 】
: 才知道这个...
: 原来set的iterator也是const_iterator,两者是相同的,而只有unordered_set的iter
: ator才可以修改
: ...................
--
修改:z16166 FROM 61.48.130.*
FROM 61.48.130.*
MSVC里弄了个条件定义,set时确实也是iterator = _Tree_const_iterator<_Scary_val>
template <class _Traits>
class _Tree { // ordered red-black tree for map/multimap/set/multiset
public:
static constexpr bool _Is_set = is_same_v<key_type, value_type>;
public:
using iterator = conditional_t<_Is_set, _Tree_const_iterator<_Scary_val>, _Tree_iterator<_Scary_val>>;
【 在 gameplayer 的大作中提到: 】
: 差不多,glibc中set的iterator和const_iterator是相同的,在stl_set.h中有:
: // _GLIBCXX_RESOLVE_LIB_DEFECTS
: // DR 103. set::iterator is required to be modifiable,
: ...................
--
FROM 61.48.130.*