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
}