C++ 无序集非常量迭代器
出于测试目的,我创建了一个无序的_集,并尝试在该集上迭代。集合拥有自己的类:C++ 无序集非常量迭代器,c++,c++11,stl,unordered-set,C++,C++11,Stl,Unordered Set,出于测试目的,我创建了一个无序的_集,并尝试在该集上迭代。集合拥有自己的类: class Student { private: int matrNr; string name; public: Student( const int& matrNr = 0, const string& name = "" ) : matrNr( matrNr ), name( name ) {} void setNr( const
class Student {
private:
int matrNr;
string name;
public:
Student( const int& matrNr = 0, const string& name = "" )
: matrNr( matrNr ), name( name ) {}
void setNr( const int& matrNr ) {
this->matrNr = matrNr;
}
...
};
我插入了一些元素,并尝试在迭代过程中更改对象:
unordered_set<Student, meinHash> meineHashTable;
meineHashTable.emplace( 12, "Fred" );
meineHashTable.emplace( 22, "Barney" );
meineHashTable.emplace( 33, "Wilma" );
for (int i = 0; i < meineHashTable.bucket_count(); i++) {
cout << "Bucketnummer: " << i << endl;
unordered_set<Student, meinHash>::local_iterator iter; // not constant?!?
if (meineHashTable.bucket_size( i ) > 0) {
for (iter = meineHashTable.begin( i ); iter != meineHashTable.end( i ); iter++) {
//const_cast<Student&>(*iter).setNr( 1234 ); //This does work
iter->setNr( 1234 ); //This does not work
}
}
else {
cout << "An empty Bucket" << endl;
}
}
对于将来有相同问题的本主题查找者,如果您使用暴力更改matrNr,以下是一些示例输出:
const_cast<Student&>(*iter).setNr( 5 );
const_cast(*iter).setNr(5);
并尝试显示它:
unordered_set<Student, meinHash>::local_iterator iter = meineHashTable.find( 5 );
iter->display();
无序集合::局部迭代器iter=meineHashTable.find(5);
iter->display();
您可能会得到如下结果:
巴克特努默:0
空桶
巴克特努默:1
女:5
姓名:威尔玛
巴克特努默:2
空桶
巴克特努默:3
空桶
巴克特努默:4
女:5
姓名:弗雷德
巴克特努默:5
空桶
巴克特努默:6
女:5
姓名:巴尼
巴克特努默:7
空桶
//不需要的输出;-)
Matrikelnummer:-84215041
姓名:
无序集合是一种数据结构,在这种结构中,如果不更改项目的位置,则无法修改项目 非常量迭代器在这里是常量,因为STL确实可以防止您犯这样明显的错误
如果要修改无序集合的项,必须将其删除,然后重新添加。它们的值类型是
集合的常量K
,而映射的值类型是对;无序版本也是如此
迭代器提供对value\u类型&
的访问权限,以及对const-value\u类型&
的常量迭代器的访问权限。如您所见,这两种迭代器类型都不能“撤消”键的常量
密钥不可变的原因是它构成了底层数据结构的一个组成部分;更改密钥将需要一次非常重要的内部重新排列,这将导致各种问题(例如,非零计算复杂性(对于元素访问!),以及迭代器顺序混乱)。集和无序集都具有只读密钥。很容易理解为什么会出现这种情况——如果键值发生变化,数据结构会将其归档到错误的位置,您将无法再找到它
根据您的示例,假设哈希函数只返回matrNr
字段。当散列数更改时,对1234
的任何查找都将失败,因为该散列桶中没有存储任何内容
可以更改对象中未用于生成哈希键的部分,但这可能会导致难以跟踪错误。标准委员会决定通过使整个关键组件
有两种方法可以绕过这一限制。第一种方法是将键与值分开,并使用映射
或无序映射
。第二种方法是从集合中删除该项,并在修改后重新插入。我遇到了类似的问题,我也感到困惑。我查看的所有源代码都表明,std::unordered\u set::find
可以返回一个非常量迭代器,该迭代器将取消对value\u type&
的引用,即非const
。另一方面,上面所有的答案都表明,在实例中更改字段值会更改其哈希值,因此它的存储方式似乎不可能做到这一点。规范提供了一个无法使用的接口,这似乎是非常“草率”的,因此必须有一种方法来做提问者想要做的事情,而且确实有。您只需向编译器提供足够的信息,就可以知道为您提供非常量迭代器是安全的。为了进一步简化原来的问题,我们考虑如下:
struct student {
std::string name;
double gpa;
// necessary for a decent member of a hash table. Compares all fields by default
bool operator==(const student& other) const = default;
student(const char* _name)
: name(_name)
, gpa(2.0) {}
};
std::unordered_set<student> student_set;
auto found = student_set.find("edgar"); // danger!! See note below
if (found != student_set.end()) {
found->gpa = 4.0; // <- compile failure here. "found" is of type const_iterator
}
对我来说(使用clang),这还不够——有必要,但还不够,但至少编译器现在知道更改“gpa”不会对哈希表中记录的索引产生影响。然后,我不得不使用带有“gpa”声明的mutable
关键字来明确地告诉编译器,该字段可以更改,而不会更改我们编写者认为的数据状态。通常,它用于引用计数或主指针以及其他类型的元数据,这些元数据不是结构实例的固有状态,但在这里也适用。现在我们有了-
struct student {
std::string name;
mutable double gpa;
// indicates that a matching name means a hit
bool operator==(const student& other) const {
return name.compare(other.name) == 0;
}
student(const char* _name)
: name(_name)
, gpa(2.0) {}
};
std::unordered_set<student, student_hash> student_set;
auto found = student_set.find("edgar"); // will find "edgar" regardless of gpa
if (found != student_set.end()) {
found->gpa = 4.0; // <- no longer fails here. "found" is of type iterator
}
struct学生{
std::字符串名;
可变双gpa;
//指示匹配的名称表示命中
布尔运算符==(常量学生和其他)常量{
返回name.compare(other.name)==0;
}
学生(常量字符*\u名称)
:name(_name)
,gpa(2.0){}
};
std::无序的学生集;
自动查找=学生集。查找(“埃德加”);//无论gpa如何,都会找到“edgar”
if(find!=student\u set.end()){
found->gpa=4.0;//您可以将常量类型强制转换为非常量类型。这样您就“告诉编译器”您知道自己在做什么,所以您确实应该知道自己在做什么。当“这不起作用”时,它如何失败?编译时还是运行时?错误消息?呃,即使您更改了顺序,迭代器也不会失效(它们基本上是指向节点的指针)。@Xeo:我的意思是,如果你在循环中迭代,如果你明白我的意思,那么迭代顺序就会变得混乱。你猜对了,matrNr用于哈希;-)。我想我理解这个问题。如果我在迭代过程中将所有对象的matrNr更改为5(使用常量转换)后来我试图找到它,我导致了未定义的行为,因为之前桶号5是空的。我做了很多编辑,因为我的用例有一些必要的东西,其他原因在我的配对用例中是必要的(因此,我不知道它们是必要的)也就是说,操作符==
。我能说什么呢?我不是编译器。迭代器的问题是不可编辑,它不是常量迭代器
struct student_hash {
std::size_t operator()(const student& hashed_student) {
return std::hash<std::string>()(hashed_student.name);
}
};
struct student {
std::string name;
mutable double gpa;
// indicates that a matching name means a hit
bool operator==(const student& other) const {
return name.compare(other.name) == 0;
}
student(const char* _name)
: name(_name)
, gpa(2.0) {}
};
std::unordered_set<student, student_hash> student_set;
auto found = student_set.find("edgar"); // will find "edgar" regardless of gpa
if (found != student_set.end()) {
found->gpa = 4.0; // <- no longer fails here. "found" is of type iterator
}