我可以在 std::list 中移动元素而不会使迭代器或引用无效,但是如何移动呢?

I can move elements within the std::list without invalidating iterators nor references, but how?

本文关键字:移动 无效 何移动 引用 迭代器 list std 元素 我可以      更新时间:2023-10-16

来自 cpppreferences 文章 onstd::list

在列表中或跨多个列表添加、删除和移动元素不会使迭代器或引用失效。仅当删除相应的元素时,迭代器才会失效。

事实上,在对元素进行排序时就是这种情况。来自 cpp偏好文章 关于std::list::sort

此函数与std::sort函数的不同之处在于,它不要求列表的元素类型可交换,保留所有迭代器的值,并执行稳定的排序。

但是,如何在保留所有迭代器的值的同时任意交换两个元素的位置呢?

例如,假设我有一个列表:

std::list<int> l({1, 2, 3, 4});
auto it = l.begin(), jt = ++l.begin();

现在it指向1jt指向2。我可以重新排序此列表,以便2排在1之前,但it仍然指向1

我可以做:

std::swap(*it, *jt);

但是,虽然2将在1之前出现,但我不会保留迭代器的值,因为显然it会指向2

鉴于上述来自cpp偏好的引用,我想应该有可能实现我想要实现的目标;但是如何实现呢?

编辑:为了弄清楚事情:再举一个例子:

std::list<int> l({2, 1, 3, 4, 5});
auto it = l.begin(), jt = ++l.begin();

现在it指向2jt指向1

std::list::sort具有我正在寻找的语义:

l.sort();

现在列表的顺序是:1, 2, 3, 4, 5,但是it仍然指向2jt仍然指向1

另一方面,std::swap(it, jt)norstd::swap(*it, *jt)没有我想要的语义。打电话给他们中的任何一个都会使it指向1jt指向2。(鉴定)

但是,如何在保留所有迭代器值的同时任意交换两个元素的位置呢?

按照@cpplearner的建议,使用.splice().

它可以对单个元素或范围进行操作。它还可以跨列表传输元素。

下面是一个演示如何移动单个元素的简单示例。

std::list<int> list{1,2,3,4,5};
// This element will be moved
auto source = std::find(list.begin(), list.end(), 4);
// It will be inserted before this element
auto destination = std::find(list.begin(), list.end(), 2);
list.splice(destination, list, source);
// ^                      ^
// |                      `- A list to move from
// `- A list to move to   
// Prints `1 4 2 3 5`.
for (int it : list) std::cout << it << ' ';

您正在交换值。 不是迭代器值。 因此,迭代器保持不变,而它们指向的值会发生变化(被交换)。

至少在这方面,请考虑像指针这样的迭代器。

因此,交换迭代器和值的方法就是:

std::swap(*it, *jt);
std::swap(it, jt);