赋值(operator =)使容器的迭代器失效
Assignment (operator =) invalidates iterators for containers
我有这样的代码:
std::vector<int> v1 = { 1, 2, 3, 4 };
std::vector<int> v2 = { 7, 8, 9, 10 };
std::vector<int>::iterator it = std::next(v1.begin());
v1 = v2;
int test = *it;
std::cout << test;
以上代码会抛出错误:迭代器不可取消引用。
但是,如果我用列表替换向量,如下所示:
std::list<int> v1 = { 1, 2, 3, 4 };
std::list<int> v2 = { 7, 8, 9, 10 };
std::list<int>::iterator it = std::next(v1.begin());
v1 = v2;
int test = *it;
std::cout << test;
代码正好 运行 符合预期,没有错误。
从Iterator invalidation rules, and the std::list::operator=,我被告知在调用operator =之后,与这个容器相关的所有迭代器、引用和指针都失效了,除了结束迭代器。但是为什么上面std::list 的代码有效吗?我是不是误解了一些重要的东西?
当迭代器失效时,取消引用它是未定义的行为。因此,就规范而言,您在这样做时观察到的任何行为都是无关紧要的。 "Working",根据您的预期,属于允许的行为。
FWIW(不多,待定),我希望 std::list
赋值运算符的实现等同于这样的东西:
list& operator=(list const& rhs) {
if (this == &rhs)
return *this;
auto lhs_i = begin();
auto rhs_i = rhs.begin();
// write over the elements in any currently existing nodes, this
// avoids any unnecessary allocations
while (lhs_i != end() && rhs_i != rhs.end()) {
*lhs_i++ = *rhs_i++;
}
// erase any extra elements if size() > rhs.size()
erase(lhs_i, end());
// push back additional elements if size() < rhs.size()
while (rhs_i != rhs.end()) {
push_back(*rhs_i++);
}
return *this;
}
如果是,您可以看到,对于像您这样的情况,列表都具有相同数量的元素,没有创建或销毁任何元素,因此您非常希望迭代器能够完全正常工作。当然,这完全是猜测,绝对不是您应该依赖的行为,因为即使您的实施情况如此,他们也可以在下一个版本中更改它,恕不另行通知。
这是未定义的行为,但 GCC 有调试容器来捕捉这种行为。
启用-D_GLIBCXX_DEBUG
:
int test = *it;
^~~~
/usr/local/include/c++/6.1.0/debug/safe_iterator.h:270:
Error: attempt to dereference a singular iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7fff5f561e90 {
type = __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
state = singular;
references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7fff5f561ef0
}
bash: line 7: 16071 Aborted (core dumped) ./a.out
我有这样的代码:
std::vector<int> v1 = { 1, 2, 3, 4 };
std::vector<int> v2 = { 7, 8, 9, 10 };
std::vector<int>::iterator it = std::next(v1.begin());
v1 = v2;
int test = *it;
std::cout << test;
以上代码会抛出错误:迭代器不可取消引用。
但是,如果我用列表替换向量,如下所示:
std::list<int> v1 = { 1, 2, 3, 4 };
std::list<int> v2 = { 7, 8, 9, 10 };
std::list<int>::iterator it = std::next(v1.begin());
v1 = v2;
int test = *it;
std::cout << test;
代码正好 运行 符合预期,没有错误。
从Iterator invalidation rules, and the std::list::operator=,我被告知在调用operator =之后,与这个容器相关的所有迭代器、引用和指针都失效了,除了结束迭代器。但是为什么上面std::list 的代码有效吗?我是不是误解了一些重要的东西?
当迭代器失效时,取消引用它是未定义的行为。因此,就规范而言,您在这样做时观察到的任何行为都是无关紧要的。 "Working",根据您的预期,属于允许的行为。
FWIW(不多,待定),我希望 std::list
赋值运算符的实现等同于这样的东西:
list& operator=(list const& rhs) {
if (this == &rhs)
return *this;
auto lhs_i = begin();
auto rhs_i = rhs.begin();
// write over the elements in any currently existing nodes, this
// avoids any unnecessary allocations
while (lhs_i != end() && rhs_i != rhs.end()) {
*lhs_i++ = *rhs_i++;
}
// erase any extra elements if size() > rhs.size()
erase(lhs_i, end());
// push back additional elements if size() < rhs.size()
while (rhs_i != rhs.end()) {
push_back(*rhs_i++);
}
return *this;
}
如果是,您可以看到,对于像您这样的情况,列表都具有相同数量的元素,没有创建或销毁任何元素,因此您非常希望迭代器能够完全正常工作。当然,这完全是猜测,绝对不是您应该依赖的行为,因为即使您的实施情况如此,他们也可以在下一个版本中更改它,恕不另行通知。
这是未定义的行为,但 GCC 有调试容器来捕捉这种行为。
启用-D_GLIBCXX_DEBUG
:
int test = *it;
^~~~
/usr/local/include/c++/6.1.0/debug/safe_iterator.h:270:
Error: attempt to dereference a singular iterator.
Objects involved in the operation:
iterator "this" @ 0x0x7fff5f561e90 {
type = __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
state = singular;
references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7fff5f561ef0
}
bash: line 7: 16071 Aborted (core dumped) ./a.out