list::iterator 对移至列表无效?
list::iterator invalid for moved-to list?
我使用了相当多的 C++,但没那么多 std::list
..
在我当前的项目中,我需要一个 std::list<..>
数据成员,并使用 std::list<..>::iterator
跟踪列表中的位置。该对象还必须是可移动的,但在我的情况下,默认的移动构造函数是不可能的。这里 std::list
做了一件让我吃惊的事。
考虑
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 6);
if (pos == l.end()) cout << "l end\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.end()) cout << "l end\n";
if (pos == lmv.end()) cout << "lmv end\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
这输出
___vector___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
lmv end
___list___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
l end
即指向移出 list
s 端的迭代器不指向移至 list
s 端。
但它对 vector
有效,如果迭代器本质上是指针,这是我一直期望的。为什么 list
不同?元素的内存位置不应随移动而改变.. list
s move 是否会更改列表迭代器?为什么?
我正在使用“g++.exe(Rev1,由 MSYS2 项目构建)10.2.0”
在 Windows 10
的 MSYS2 下
如果你在测试函数的末尾添加了如此可怕的行
(这是完全不正确的,消毒剂会侮辱你!),
你可以看到在 vector
的情况下 end()
迭代器
指定缓冲区的 past-the-end 包含的内容
存储的元素,但在 list
的情况下是结束迭代器
指定存储在 list
中的某种标记
结构本身。
那么移动之后,vector的buffer还是一样的
但它不再属于 l
,所以地址 past-the-end
此缓冲区的等价于 end()
for lmv
.
另一方面,移动list
后,指定pos
地址 inside l
仍然指定相同的地址(尽管
l
被移出)但没有指定里面的 end()
标记
lvm
在初始化 pos
时甚至不存在。
std::cout << "pos: " << (void *)(&*pos) << '\n';
std::cout << "l: " << (void *)(&l) << '\n';
std::cout << "l.begin(): " << (void *)(&*l.begin()) << '\n';
std::cout << "l.end(): " << (void *)(&*l.end()) << '\n';
std::cout << "lmv: " << (void *)(&lmv) << '\n';
std::cout << "lmv.begin(): " << (void *)(&*lmv.begin()) << '\n';
std::cout << "lmv.end(): " << (void *)(&*lmv.end()) << '\n';
移动容器时应保留迭代器。
但是 end
容器的迭代器不指向元素,因此允许 invalidated when moving a container.
如果您将代码更改为使用 begin
而不是 end
,那么它可以工作 as you expect。
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 1);
if (pos == l.begin()) cout << "l begin\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.begin()) cout << "l begin\n";
if (pos == lmv.begin()) cout << "lmv begin\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
请注意,比较来自两个不同容器的迭代器是未定义的行为,因此最终的 pos == l.begin()
是未定义的行为,并且 visual studio 的调试构建至少会在 运行 时抛出断言代码。
我想你的原始代码是有效的,因为 std::vector
end
迭代器通常只是实现为指向最后一个元素之后的一个。我会想象 std::list
结束迭代器包含一个空指针和一个指向列表的指针。
我使用了相当多的 C++,但没那么多 std::list
..
在我当前的项目中,我需要一个 std::list<..>
数据成员,并使用 std::list<..>::iterator
跟踪列表中的位置。该对象还必须是可移动的,但在我的情况下,默认的移动构造函数是不可能的。这里 std::list
做了一件让我吃惊的事。
考虑
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 6);
if (pos == l.end()) cout << "l end\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.end()) cout << "l end\n";
if (pos == lmv.end()) cout << "lmv end\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
这输出
___vector___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
lmv end
___list___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
l end
即指向移出 list
s 端的迭代器不指向移至 list
s 端。
但它对 vector
有效,如果迭代器本质上是指针,这是我一直期望的。为什么 list
不同?元素的内存位置不应随移动而改变.. list
s move 是否会更改列表迭代器?为什么?
我正在使用“g++.exe(Rev1,由 MSYS2 项目构建)10.2.0” 在 Windows 10
的 MSYS2 下如果你在测试函数的末尾添加了如此可怕的行
(这是完全不正确的,消毒剂会侮辱你!),
你可以看到在 vector
的情况下 end()
迭代器
指定缓冲区的 past-the-end 包含的内容
存储的元素,但在 list
的情况下是结束迭代器
指定存储在 list
中的某种标记
结构本身。
那么移动之后,vector的buffer还是一样的
但它不再属于 l
,所以地址 past-the-end
此缓冲区的等价于 end()
for lmv
.
另一方面,移动list
后,指定pos
地址 inside l
仍然指定相同的地址(尽管
l
被移出)但没有指定里面的 end()
标记
lvm
在初始化 pos
时甚至不存在。
std::cout << "pos: " << (void *)(&*pos) << '\n';
std::cout << "l: " << (void *)(&l) << '\n';
std::cout << "l.begin(): " << (void *)(&*l.begin()) << '\n';
std::cout << "l.end(): " << (void *)(&*l.end()) << '\n';
std::cout << "lmv: " << (void *)(&lmv) << '\n';
std::cout << "lmv.begin(): " << (void *)(&*lmv.begin()) << '\n';
std::cout << "lmv.end(): " << (void *)(&*lmv.end()) << '\n';
移动容器时应保留迭代器。
但是 end
容器的迭代器不指向元素,因此允许 invalidated when moving a container.
如果您将代码更改为使用 begin
而不是 end
,那么它可以工作 as you expect。
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 1);
if (pos == l.begin()) cout << "l begin\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.begin()) cout << "l begin\n";
if (pos == lmv.begin()) cout << "lmv begin\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
请注意,比较来自两个不同容器的迭代器是未定义的行为,因此最终的 pos == l.begin()
是未定义的行为,并且 visual studio 的调试构建至少会在 运行 时抛出断言代码。
我想你的原始代码是有效的,因为 std::vector
end
迭代器通常只是实现为指向最后一个元素之后的一个。我会想象 std::list
结束迭代器包含一个空指针和一个指向列表的指针。